]> jfr.im git - irc/evilnet/x3.git/blob - src/chanserv.c
make regex case sensitive, and old memoserv fix
[irc/evilnet/x3.git] / src / chanserv.c
1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21 #include "chanserv.h"
22 #include "conf.h"
23 #include "global.h"
24 #include "modcmd.h"
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "saxdb.h"
27 #include "spamserv.h"
28 #include "timeq.h"
29
30 #define CHANSERV_CONF_NAME "services/chanserv"
31
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
43 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60
61 /* ChanServ database */
62 #define KEY_VERSION_CONTROL "version_control"
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
65
66 /* version control paramiter */
67 #define KEY_VERSION_NUMBER "version_number"
68
69 /* Note type parameters */
70 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
71 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
72 #define KEY_NOTE_SETTER_ACCESS "setter_access"
73 #define KEY_NOTE_VISIBILITY "visibility"
74 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
75 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
76 #define KEY_NOTE_VIS_ALL "all"
77 #define KEY_NOTE_MAX_LENGTH "max_length"
78 #define KEY_NOTE_SETTER "setter"
79 #define KEY_NOTE_NOTE "note"
80
81 /* Do-not-register channels */
82 #define KEY_DNR "dnr"
83 #define KEY_DNR_SET "set"
84 #define KEY_DNR_SETTER "setter"
85 #define KEY_DNR_REASON "reason"
86
87 /* Channel data */
88 #define KEY_REGISTERED "registered"
89 #define KEY_REGISTRAR "registrar"
90 #define KEY_SUSPENDED "suspended"
91 #define KEY_PREVIOUS "previous"
92 #define KEY_SUSPENDER "suspender"
93 #define KEY_ISSUED "issued"
94 #define KEY_REVOKED "revoked"
95 #define KEY_SUSPEND_EXPIRES "suspend_expires"
96 #define KEY_SUSPEND_REASON "suspend_reason"
97 #define KEY_GIVEOWNERSHIP "giveownership"
98 #define KEY_STAFF_ISSUER "staff_issuer"
99 #define KEY_OLD_OWNER "old_owner"
100 #define KEY_TARGET "target"
101 #define KEY_TARGET_ACCESS "target_access"
102 #define KEY_VISITED "visited"
103 #define KEY_TOPIC "topic"
104 #define KEY_GREETING "greeting"
105 #define KEY_USER_GREETING "user_greeting"
106 #define KEY_MODES "modes"
107 #define KEY_FLAGS "flags"
108 #define KEY_OPTIONS "options"
109 #define KEY_USERS "users"
110 #define KEY_BANS "bans" /* for lamers */
111 #define KEY_MAX "max"
112 #define KEY_NOTES "notes"
113 #define KEY_TOPIC_MASK "topic_mask"
114 #define KEY_OWNER_TRANSFER "owner_transfer"
115
116 /* User data */
117 #define KEY_LEVEL "level"
118 #define KEY_INFO "info"
119 #define KEY_SEEN "seen"
120 #define KEY_ACCESSEXPIRY "accessexpiry"
121 #define KEY_CLVLEXPIRY "clvlexpiry"
122 #define KEY_LASTLEVEL "lastlevel"
123
124 /* Ban data */
125 #define KEY_OWNER "owner"
126 #define KEY_REASON "reason"
127 #define KEY_SET "set"
128 #define KEY_DURATION "duration"
129 #define KEY_EXPIRES "expires"
130 #define KEY_TRIGGERED "triggered"
131
132 #define KEY_GOD_TIMEOUT "god_timeout"
133
134 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
135 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
136 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
137
138 /* Administrative messages */
139 static const struct message_entry msgtab[] = {
140 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
141
142 /* Channel registration */
143 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
144 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
145 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
146 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
147 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
148 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
149 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
150 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
151
152 /* Do-not-register channels */
153 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
154 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
155 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
156 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
157 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
158 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
159 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
160 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
161 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
162 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
163 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
164 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
165 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
166 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
167
168 /* Channel unregistration */
169 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
170 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
171 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
172 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
173
174 /* Channel moving */
175 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
176 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
177
178 /* Channel merging */
179 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
180 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
181 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
182 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
183 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
184
185 /* Handle unregistration */
186 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
187
188 /* Error messages */
189 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
190 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
191 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
192 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
193 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
194 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
195 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
196 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
197 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
198 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
199 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
200 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
201 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
202 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
203
204 /* Removing yourself from a channel. */
205 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
206 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
207 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
208
209 /* User management */
210 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
211 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
212 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
213 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
214 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
215 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
216 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
217 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
218 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
219 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
220 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
221 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
222 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
223 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
224 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
225 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
226 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
227
228 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
229 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
230 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
231 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
232 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
233 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
234 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
235 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
236 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
237
238 /* Ban management */
239 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
240 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
241 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
242 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
243 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
244 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
245 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
246 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
247 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
248 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
249 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
250 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
251 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
252 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
253 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
254 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
255 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
256
257 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
258
259 /* Channel management */
260 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
261 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
262 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
263
264 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
265 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
266 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
267 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
268 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
269 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
270 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
271
272 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
273 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
274 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
275 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
276 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
277 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
278 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
279 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
280 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
281 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
282 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
283 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
284 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
285 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
286 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
287 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
288 { "CSMSG_SET_MODES", "$bModes $b %s" },
289 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
290 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
291 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
292 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
293 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
294 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
295 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
296 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
297 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
298 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
299 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
300 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
301 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
302 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
303 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
304 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
305 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
306 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
307 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
308 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
309
310 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
311 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
312 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
313 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
314 { "CSMSG_USET_INFO", "$bInfo $b %s" },
315
316 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
317 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
318 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
319 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
320 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
321 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
322 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
323 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
324 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
325 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
326 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
327
328 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
329 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
330 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
331 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
332 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
333 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
334 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
335
336 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
337 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
338 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
339 { "CSMSG_PROTECT_NONE", "No users will be protected." },
340 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
341 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
342 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
343
344 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
345 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
346 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
347 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
348 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
349
350 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
351 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
352 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
353 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
354 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
355
356 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
357 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
358 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
359 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
360 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
361
362 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
363 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
364 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
365 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
366 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
367 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
368
369 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
370 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
371 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
372 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
373 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
374 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
375 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
376 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
377 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
378
379 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
380 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
381 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
382
383 /* Channel userlist */
384 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
385 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
386 /* uncomment if needed to adujust styles (and change code below)
387 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
388 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
389 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
390 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
391 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
392 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
393 */
394 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
395 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
396 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
397
398 /* Channel note list */
399 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
400 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
401 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
402 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
403 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
404 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
405 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
406 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
407 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
408 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
409 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
410 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
411 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
412 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
413 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
414
415 /* Channel [un]suspension */
416 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
417 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
418 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
419 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
420 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
421 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
422 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
423
424 /* Access information */
425 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
426 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
427 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
428 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
429 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
430 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
431 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
432 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
433 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
434 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
435 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
436 { "CSMSG_UC_H_TITLE", "network helper" },
437 { "CSMSG_LC_H_TITLE", "support helper" },
438 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
439
440 /* Seen information */
441 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
442 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
443 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
444 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
445
446 /* Names information */
447 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
448 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
449
450 /* Channel information */
451 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
452 { "CSMSG_BAR", "----------------------------------------"},
453 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
454 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
455 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
456 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
457 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
458 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
459 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
460 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
461 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
462 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
463 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
464 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
465 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
466 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
467 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
468 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
469 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
470 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
471 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
472 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
473 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
474 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
475 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
476 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
477 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
478 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
479
480 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
481 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
482 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
483 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
484 { "CSMSG_PEEK_OPS", "$bOps:$b" },
485 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
486 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
487
488 /* Network information */
489 { "CSMSG_NETWORK_INFO", "Network Information:" },
490 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
491 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
492 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
493 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
494 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
495 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
496 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
497 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
498
499 /* Staff list */
500 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
501 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
502 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
503
504 /* Channel searches */
505 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
506 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
507 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
508 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
509
510 /* Channel configuration */
511 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
512 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
513 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
514 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
515 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
516
517 /* User settings */
518 { "CSMSG_USER_OPTIONS", "User Options:" },
519 // { "CSMSG_USER_PROTECTED", "That user is protected." },
520
521 /* Toys */
522 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
523 { "CSMSG_PING_RESPONSE", "Pong!" },
524 { "CSMSG_WUT_RESPONSE", "wut" },
525 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
526 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
527 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
528 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
529 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
530 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
531 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
532
533 /* Other things */
534 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
535 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
536 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
537 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
538 { NULL, NULL }
539 };
540
541 /* eject_user and unban_user flags */
542 #define ACTION_KICK 0x0001
543 #define ACTION_BAN 0x0002
544 #define ACTION_ADD_LAMER 0x0004
545 #define ACTION_ADD_TIMED_LAMER 0x0008
546 #define ACTION_UNBAN 0x0010
547 #define ACTION_DEL_LAMER 0x0020
548
549 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
550 #define MODELEN 40 + KEYLEN
551 #define PADLEN 21
552 #define ACCESSLEN 10
553
554 #define CSFUNC_ARGS user, channel, argc, argv, cmd
555
556 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
557 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
558 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
559 reply("MSG_MISSING_PARAMS", argv[0]); \
560 CHANSERV_SYNTAX(); \
561 return 0; }
562
563 DECLARE_LIST(dnrList, struct do_not_register *);
564 DEFINE_LIST(dnrList, struct do_not_register *);
565
566 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
567
568 struct userNode *chanserv;
569 dict_t note_types;
570 int off_channel;
571 extern struct string_list *autojoin_channels;
572 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
573 static struct log_type *CS_LOG;
574 struct adduserPending* adduser_pendings = NULL;
575 unsigned int adduser_pendings_count = 0;
576 unsigned long god_timeout;
577
578 static struct
579 {
580 struct channelList support_channels;
581 struct mod_chanmode default_modes;
582
583 unsigned long db_backup_frequency;
584 unsigned long channel_expire_frequency;
585 unsigned long dnr_expire_frequency;
586 unsigned long ban_timeout_frequency;
587
588 long info_delay;
589 unsigned int adjust_delay;
590 long channel_expire_delay;
591 unsigned int nodelete_level;
592
593 unsigned int adjust_threshold;
594 int join_flood_threshold;
595
596 unsigned int greeting_length;
597 unsigned int refresh_period;
598 unsigned int giveownership_period;
599
600 unsigned int max_owned;
601 unsigned int max_chan_users;
602 unsigned int max_chan_bans; /* lamers */
603 unsigned int max_userinfo_length;
604
605 struct string_list *set_shows;
606 struct string_list *eightball;
607 struct string_list *old_ban_names;
608
609 const char *ctcp_short_ban_duration;
610 const char *ctcp_long_ban_duration;
611
612 const char *irc_operator_epithet;
613 const char *network_helper_epithet;
614 const char *support_helper_epithet;
615 } chanserv_conf;
616
617 struct listData
618 {
619 struct userNode *user;
620 struct userNode *bot;
621 struct chanNode *channel;
622 const char *search;
623 unsigned short lowest;
624 unsigned short highest;
625 struct userData **users;
626 struct helpfile_table table;
627 };
628
629 enum note_access_type
630 {
631 NOTE_SET_CHANNEL_ACCESS,
632 NOTE_SET_CHANNEL_SETTER,
633 NOTE_SET_PRIVILEGED
634 };
635
636 enum note_visible_type
637 {
638 NOTE_VIS_ALL,
639 NOTE_VIS_CHANNEL_USERS,
640 NOTE_VIS_PRIVILEGED
641 };
642
643 struct note_type
644 {
645 enum note_access_type set_access_type;
646 union {
647 unsigned int min_opserv;
648 unsigned short min_ulevel;
649 } set_access;
650 enum note_visible_type visible_type;
651 unsigned int max_length;
652 unsigned int refs;
653 char name[1];
654 };
655
656 struct note
657 {
658 struct note_type *type;
659 char setter[NICKSERV_HANDLE_LEN+1];
660 char note[1];
661 };
662
663 static unsigned int registered_channels;
664 static unsigned int banCount;
665
666 static const struct {
667 char *name;
668 char *title;
669 unsigned short level;
670 char ch;
671 } accessLevels[] = { /* MUST be orderd less to most! */
672 { "peon", "Peon", UL_PEON, '+' },
673 { "halfop", "HalfOp", UL_HALFOP, '%' },
674 { "op", "Op", UL_OP, '@' },
675 { "manager", "Manager", UL_MANAGER, '%' },
676 { "coowner", "Coowner", UL_COOWNER, '*' },
677 { "owner", "Owner", UL_OWNER, '!' },
678 { "helper", "BUG:", UL_HELPER, 'X' }
679 };
680
681 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
682 static const struct {
683 char *format_name;
684 char *db_name;
685 unsigned short default_value;
686 unsigned int old_idx;
687 unsigned int old_flag;
688 unsigned short flag_value;
689 } levelOptions[] = {
690 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
691 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
692 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
693 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
694 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
695 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
696 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
697 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
698 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
699 };
700
701 struct charOptionValues {
702 char value;
703 char *format_name;
704 } automodeValues[] = {
705 { 'n', "CSMSG_AUTOMODE_NONE" },
706 { 'y', "CSMSG_AUTOMODE_NORMAL" },
707 { 'v', "CSMSG_AUTOMODE_VOICE" },
708 { 'h', "CSMSG_AUTOMODE_HOP" },
709 { 'o', "CSMSG_AUTOMODE_OP" },
710 { 'm', "CSMSG_AUTOMODE_MUTE" },
711 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
712 }, protectValues[] = {
713 { 'a', "CSMSG_PROTECT_ALL" },
714 { 'e', "CSMSG_PROTECT_EQUAL" },
715 { 'l', "CSMSG_PROTECT_LOWER" },
716 { 'n', "CSMSG_PROTECT_NONE" }
717 }, toysValues[] = {
718 { 'd', "CSMSG_TOYS_DISABLED" },
719 { 'n', "CSMSG_TOYS_PRIVATE" },
720 { 'p', "CSMSG_TOYS_PUBLIC" }
721 }, topicRefreshValues[] = {
722 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
723 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
724 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
725 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
726 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
727 }, ctcpReactionValues[] = {
728 { 'n', "CSMSG_CTCPREACTION_NONE" },
729 { 'k', "CSMSG_CTCPREACTION_KICK" },
730 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
731 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
732 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
733 }, banTimeoutValues[] = {
734 { '0', "CSMSG_BANTIMEOUT_NONE" },
735 { '1', "CSMSG_BANTIMEOUT_10M" },
736 { '2', "CSMSG_BANTIMEOUT_2H" },
737 { '3', "CSMSG_BANTIMEOUT_4H" },
738 { '4', "CSMSG_BANTIMEOUT_1D" },
739 { '5', "CSMSG_BANTIMEOUT_1W" }
740 },
741 resyncValues[] = {
742 { 'n', "CSMSG_RESYNC_NEVER" },
743 { '1', "CSMSG_RESYNC_3_HOURS" },
744 { '2', "CSMSG_RESYNC_6_HOURS" },
745 { '3', "CSMSG_RESYNC_12_HOURS" },
746 { '4', "CSMSG_RESYNC_24_HOURS" }
747 };
748
749 static const struct {
750 char *format_name;
751 char *db_name;
752 char default_value;
753 unsigned int old_idx;
754 unsigned char count;
755 struct charOptionValues *values;
756 } charOptions[] = {
757 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues), automodeValues },
758 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
759 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
760 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
761 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
762 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues },
763 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues), resyncValues },
764 };
765
766 struct userData *helperList;
767 struct chanData *channelList;
768 static struct module *chanserv_module;
769 static unsigned int userCount;
770 unsigned int chanserv_read_version = 0; /* db version control */
771
772 #define CHANSERV_DB_VERSION 2
773
774 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
775 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
776
777 unsigned short
778 user_level_from_name(const char *name, unsigned short clamp_level)
779 {
780 unsigned int level = 0, ii;
781 if(isdigit(name[0]))
782 level = strtoul(name, NULL, 10);
783 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
784 if(!irccasecmp(name, accessLevels[ii].name))
785 level = accessLevels[ii].level;
786 if(level > clamp_level)
787 return 0;
788 return level;
789 }
790
791 char *
792 user_level_name_from_level(int level)
793 {
794 unsigned int ii;
795 char* highest;
796
797 highest = "None";
798 if(level >= 1)
799 highest = "Peon";
800 for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
801 if(level >= accessLevels[ii].level)
802 highest = accessLevels[ii].title;
803 return(highest);
804 }
805
806
807 int
808 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
809 {
810 char *sep;
811 *minl = strtoul(arg, &sep, 10);
812 if(*sep == '\0')
813 {
814 *maxl = *minl;
815 return 1;
816 }
817 else if(*sep == '-')
818 {
819 *maxl = strtoul(sep+1, &sep, 10);
820 return *sep == '\0';
821 }
822 else
823 return 0;
824 }
825
826 struct userData*
827 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
828 {
829 struct userData *uData, **head;
830
831 if(!channel || !handle)
832 return NULL;
833
834 if(override && HANDLE_FLAGGED(handle, HELPING)
835 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
836 {
837 for(uData = helperList;
838 uData && uData->handle != handle;
839 uData = uData->next);
840
841 if(!uData)
842 {
843 uData = calloc(1, sizeof(struct userData));
844 uData->handle = handle;
845
846 uData->access = UL_HELPER;
847 uData->seen = 0;
848
849 uData->info = NULL;
850
851 uData->prev = NULL;
852 uData->next = helperList;
853 if(helperList)
854 helperList->prev = uData;
855 helperList = uData;
856 }
857
858 head = &helperList;
859 }
860 else
861 {
862 for(uData = channel->users; uData; uData = uData->next)
863 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
864 break;
865
866 head = &(channel->users);
867 }
868
869 if(uData && (uData != *head))
870 {
871 /* Shuffle the user to the head of whatever list he was in. */
872 if(uData->next)
873 uData->next->prev = uData->prev;
874 if(uData->prev)
875 uData->prev->next = uData->next;
876
877 uData->prev = NULL;
878 uData->next = *head;
879
880 if(*head)
881 (**head).prev = uData;
882 *head = uData;
883 }
884
885 return uData;
886 }
887
888 /* Returns non-zero if user has at least the minimum access.
889 * exempt_owner is set when handling !set, so the owner can set things
890 * to/from >500.
891 */
892 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
893 {
894 struct userData *uData;
895 struct chanData *cData = channel->channel_info;
896 unsigned short minimum = cData->lvlOpts[opt];
897 if(!minimum)
898 return 1;
899 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
900 if(!uData)
901 return 0;
902 if(minimum <= uData->access)
903 return 1;
904 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
905 return 1;
906 return 0;
907 }
908
909 /* Scan for other users authenticated to the same handle
910 still in the channel. If so, keep them listed as present.
911
912 user is optional, if not null, it skips checking that userNode
913 (for the handle_part function) */
914 static void
915 scan_user_presence(struct userData *uData, struct userNode *user)
916 {
917 struct modeNode *mn;
918
919 if(IsSuspended(uData->channel)
920 || IsUserSuspended(uData)
921 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
922 {
923 uData->present = 0;
924 }
925 else
926 {
927 uData->present = 1;
928 uData->seen = now;
929 }
930 }
931
932 static void
933 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
934 {
935 unsigned int eflags, argc;
936 char *argv[4];
937 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
938
939 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
940 if(!channel->channel_info
941 || IsSuspended(channel->channel_info)
942 || IsService(user)
943 || !ircncasecmp(text, "ACTION ", 7))
944 return;
945 /* We dont punish people we know -Rubin
946 * * Figure out the minimum level needed to CTCP the channel *
947 *
948 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
949 * return;
950 */
951 /* If they are a user of the channel, they are exempt */
952 if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
953 return;
954 /* We need to enforce against them; do so. */
955 eflags = 0;
956 argv[0] = (char*)text;
957 argv[1] = user->nick;
958 argc = 2;
959 if(GetUserMode(channel, user))
960 eflags |= ACTION_KICK;
961 switch(channel->channel_info->chOpts[chCTCPReaction]) {
962 default: case 'n': return;
963 case 'k':
964 eflags |= ACTION_KICK;
965 break;
966 case 'b':
967 eflags |= ACTION_BAN;
968 break;
969 case 't':
970 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
971 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
972 break;
973 case 'T':
974 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
975 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
976 break;
977 }
978 argv[argc++] = bad_ctcp_reason;
979 eject_user(chanserv, channel, argc, argv, NULL, eflags);
980 }
981
982 struct note_type *
983 chanserv_create_note_type(const char *name)
984 {
985 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
986 strcpy(ntype->name, name);
987 ntype->refs = 1;
988 dict_insert(note_types, ntype->name, ntype);
989 return ntype;
990 }
991
992 static void
993 chanserv_deref_note_type(void *data)
994 {
995 struct note_type *ntype = data;
996
997 if(--ntype->refs > 0)
998 return;
999 free(ntype);
1000 }
1001
1002 static void
1003 chanserv_flush_note_type(struct note_type *ntype)
1004 {
1005 struct chanData *cData;
1006 for(cData = channelList; cData; cData = cData->next)
1007 dict_remove(cData->notes, ntype->name);
1008 }
1009
1010 static void
1011 chanserv_truncate_notes(struct note_type *ntype)
1012 {
1013 struct chanData *cData;
1014 struct note *note;
1015 unsigned int size = sizeof(*note) + ntype->max_length;
1016
1017 for(cData = channelList; cData; cData = cData->next) {
1018 note = dict_find(cData->notes, ntype->name, NULL);
1019 if(!note)
1020 continue;
1021 if(strlen(note->note) <= ntype->max_length)
1022 continue;
1023 dict_remove2(cData->notes, ntype->name, 1);
1024 note = realloc(note, size);
1025 note->note[ntype->max_length] = 0;
1026 dict_insert(cData->notes, ntype->name, note);
1027 }
1028 }
1029
1030 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
1031
1032 static struct note *
1033 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
1034 {
1035 struct note *note;
1036 unsigned int len = strlen(text);
1037
1038 if(len > type->max_length) len = type->max_length;
1039 note = calloc(1, sizeof(*note) + len);
1040 note->type = type;
1041 strncpy(note->setter, setter, sizeof(note->setter)-1);
1042 memcpy(note->note, text, len);
1043 note->note[len] = 0;
1044 dict_insert(channel->notes, type->name, note);
1045 type->refs++;
1046 return note;
1047 }
1048
1049 static void
1050 chanserv_free_note(void *data)
1051 {
1052 struct note *note = data;
1053
1054 chanserv_deref_note_type(note->type);
1055 assert(note->type->refs > 0); /* must use delnote to remove the type */
1056 free(note);
1057 }
1058
1059 static MODCMD_FUNC(cmd_createnote) {
1060 struct note_type *ntype;
1061 unsigned int arg = 1, existed = 0, max_length;
1062
1063 if((ntype = dict_find(note_types, argv[1], NULL)))
1064 existed = 1;
1065 else
1066 ntype = chanserv_create_note_type(argv[arg]);
1067 if(!irccasecmp(argv[++arg], "privileged"))
1068 {
1069 arg++;
1070 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1071 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1072 }
1073 else if(!irccasecmp(argv[arg], "channel"))
1074 {
1075 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1076 if(!ulvl)
1077 {
1078 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1079 goto fail;
1080 }
1081 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1082 ntype->set_access.min_ulevel = ulvl;
1083 }
1084 else if(!irccasecmp(argv[arg], "setter"))
1085 {
1086 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1087 }
1088 else
1089 {
1090 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1091 goto fail;
1092 }
1093
1094 if(!irccasecmp(argv[++arg], "privileged"))
1095 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1096 else if(!irccasecmp(argv[arg], "channel_users"))
1097 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1098 else if(!irccasecmp(argv[arg], "all"))
1099 ntype->visible_type = NOTE_VIS_ALL;
1100 else {
1101 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1102 goto fail;
1103 }
1104
1105 if((arg+1) >= argc) {
1106 reply("MSG_MISSING_PARAMS", argv[0]);
1107 goto fail;
1108 }
1109 max_length = strtoul(argv[++arg], NULL, 0);
1110 if(max_length < 20 || max_length > 450)
1111 {
1112 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1113 goto fail;
1114 }
1115 if(existed && (max_length < ntype->max_length))
1116 {
1117 ntype->max_length = max_length;
1118 chanserv_truncate_notes(ntype);
1119 }
1120 ntype->max_length = max_length;
1121
1122 if(existed)
1123 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1124 else
1125 reply("CSMSG_NOTE_CREATED", ntype->name);
1126 return 1;
1127
1128 fail:
1129 if(!existed)
1130 dict_remove(note_types, ntype->name);
1131 return 0;
1132 }
1133
1134 static MODCMD_FUNC(cmd_removenote) {
1135 struct note_type *ntype;
1136 int force;
1137
1138 ntype = dict_find(note_types, argv[1], NULL);
1139 force = (argc > 2) && !irccasecmp(argv[2], "force");
1140 if(!ntype)
1141 {
1142 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1143 return 0;
1144 }
1145 if(ntype->refs > 1)
1146 {
1147 if(!force)
1148 {
1149 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1150 return 0;
1151 }
1152 chanserv_flush_note_type(ntype);
1153 }
1154 dict_remove(note_types, argv[1]);
1155 reply("CSMSG_NOTE_DELETED", argv[1]);
1156 return 1;
1157 }
1158
1159 static int
1160 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1161 {
1162 if(!orig)
1163 return 0;
1164 if(orig->modes_set & change->modes_clear)
1165 return 1;
1166 if(orig->modes_clear & change->modes_set)
1167 return 1;
1168 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1169 && strcmp(orig->new_key, change->new_key))
1170 return 1;
1171 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1172 && (orig->new_limit != change->new_limit))
1173 return 1;
1174 return 0;
1175 }
1176
1177 static char max_length_text[MAXLEN+1][16];
1178
1179 static struct helpfile_expansion
1180 chanserv_expand_variable(const char *variable)
1181 {
1182 struct helpfile_expansion exp;
1183
1184 if(!irccasecmp(variable, "notes"))
1185 {
1186 dict_iterator_t it;
1187 exp.type = HF_TABLE;
1188 exp.value.table.length = 1;
1189 exp.value.table.width = 3;
1190 exp.value.table.flags = 0;
1191 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1192 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1193 exp.value.table.contents[0][0] = "Note Type";
1194 exp.value.table.contents[0][1] = "Visibility";
1195 exp.value.table.contents[0][2] = "Max Length";
1196 for(it=dict_first(note_types); it; it=iter_next(it))
1197 {
1198 struct note_type *ntype = iter_data(it);
1199 int row;
1200
1201 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1202 row = exp.value.table.length++;
1203 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1204 exp.value.table.contents[row][0] = ntype->name;
1205 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1206 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1207 "unknown";
1208 if(!max_length_text[ntype->max_length][0])
1209 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1210 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1211 }
1212 return exp;
1213 }
1214
1215 exp.type = HF_STRING;
1216 exp.value.str = NULL;
1217 return exp;
1218 }
1219
1220 static struct chanData*
1221 register_channel(struct chanNode *cNode, char *registrar)
1222 {
1223 struct chanData *channel;
1224 enum levelOption lvlOpt;
1225 enum charOption chOpt;
1226
1227 channel = calloc(1, sizeof(struct chanData));
1228
1229 channel->notes = dict_new();
1230 dict_set_free_data(channel->notes, chanserv_free_note);
1231
1232 channel->registrar = strdup(registrar);
1233 channel->registered = now;
1234 channel->visited = now;
1235 channel->limitAdjusted = now;
1236 channel->ownerTransfer = now;
1237 channel->flags = CHANNEL_DEFAULT_FLAGS;
1238 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1239 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1240 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1241 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1242
1243 channel->prev = NULL;
1244 channel->next = channelList;
1245
1246 if(channelList)
1247 channelList->prev = channel;
1248 channelList = channel;
1249 registered_channels++;
1250
1251 channel->channel = cNode;
1252 LockChannel(cNode);
1253 cNode->channel_info = channel;
1254
1255 return channel;
1256 }
1257
1258 static struct userData*
1259 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info, time_t accessexpiry)
1260 {
1261 struct userData *ud;
1262
1263 if(access > UL_OWNER)
1264 return NULL;
1265
1266 ud = calloc(1, sizeof(*ud));
1267 ud->channel = channel;
1268 ud->handle = handle;
1269 ud->seen = seen;
1270 ud->access = access;
1271 ud->info = info ? strdup(info) : NULL;
1272 ud->accessexpiry = accessexpiry ? accessexpiry : 0;
1273 ud->clvlexpiry = 0;
1274 ud->lastaccess = 0;
1275
1276 ud->prev = NULL;
1277 ud->next = channel->users;
1278 if(channel->users)
1279 channel->users->prev = ud;
1280 channel->users = ud;
1281
1282 channel->userCount++;
1283 userCount++;
1284
1285 ud->u_prev = NULL;
1286 ud->u_next = ud->handle->channels;
1287 if(ud->u_next)
1288 ud->u_next->u_prev = ud;
1289 ud->handle->channels = ud;
1290
1291 ud->flags = USER_FLAGS_DEFAULT;
1292 return ud;
1293 }
1294
1295 static void unregister_channel(struct chanData *channel, const char *reason);
1296
1297 static void
1298 chanserv_expire_tempuser(void *data)
1299 {
1300 struct userData *uData = data;
1301 char *handle;
1302
1303 if (data) {
1304 handle = strdup(uData->handle->handle);
1305 if (uData->accessexpiry > 0) {
1306 if (uData->present) {
1307 struct userNode *user, *next_un = NULL;
1308 struct handle_info *hi;
1309
1310 hi = get_handle_info(handle);
1311 for (user = hi->users; user; user = next_un) {
1312 struct mod_chanmode *change;
1313 struct modeNode *mn;
1314 unsigned int count = 0;
1315
1316 send_message(user, chanserv, "CSMSG_AUTO_DELETED", chanserv->nick, uData->channel->channel->name);
1317 if (!(mn = GetUserMode(uData->channel->channel, user)) || !(mn->modes & MODE_CHANOP)) {
1318 next_un = user->next_authed;
1319 continue;
1320 }
1321
1322 change = mod_chanmode_alloc(2);
1323 change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
1324 change->args[count++].u.member = mn;
1325
1326 if (count) {
1327 change->argc = count;
1328 mod_chanmode_announce(chanserv, uData->channel->channel, change);
1329 }
1330 mod_chanmode_free(change);
1331 next_un = user->next_authed;
1332 }
1333 }
1334 del_channel_user(uData, 1);
1335 }
1336 }
1337 }
1338
1339 static void
1340 chanserv_expire_tempclvl(void *data)
1341 {
1342 struct userData *uData = data;
1343 char *handle;
1344
1345 if (data) {
1346 handle = strdup(uData->handle->handle);
1347 if (uData->clvlexpiry > 0) {
1348 int changemodes = 0;
1349 unsigned int mode = 0;
1350
1351 if (((uData->lastaccess == UL_PEON) || (uData->lastaccess == UL_HALFOP)) && (uData->access >= UL_OP)) {
1352 changemodes = 1;
1353 mode = MODE_REMOVE | MODE_CHANOP;
1354 } else if ((uData->lastaccess == UL_PEON) && (uData->access == UL_HALFOP)) {
1355 changemodes = 1;
1356 mode = MODE_REMOVE | MODE_HALFOP;
1357 } else
1358 changemodes = 0;
1359
1360 if (uData->present) {
1361 struct userNode *user, *next_un = NULL;
1362 struct handle_info *hi;
1363
1364 hi = get_handle_info(handle);
1365 for (user = hi->users; user; user = next_un) {
1366 struct mod_chanmode *change;
1367 struct modeNode *mn;
1368 unsigned int count = 0;
1369
1370 send_message(user, chanserv, "CSMSG_CLVL_EXPIRED", uData->channel->channel->name);
1371 if (!(mn = GetUserMode(uData->channel->channel, user))) {
1372 next_un = user->next_authed;
1373 continue;
1374 }
1375
1376 if (changemodes == 0) {
1377 next_un = user->next_authed;
1378 continue;
1379 }
1380
1381 change = mod_chanmode_alloc(2);
1382 change->args[count].mode = mode;
1383 change->args[count++].u.member = mn;
1384
1385 if (count) {
1386 change->argc = count;
1387 mod_chanmode_announce(chanserv, uData->channel->channel, change);
1388 }
1389 mod_chanmode_free(change);
1390 next_un = user->next_authed;
1391 }
1392 }
1393
1394 uData->access = uData->lastaccess;
1395 uData->lastaccess = 0;
1396 uData->clvlexpiry = 0;
1397 }
1398 }
1399 }
1400
1401 void
1402 del_channel_user(struct userData *user, int do_gc)
1403 {
1404 struct chanData *channel = user->channel;
1405
1406 channel->userCount--;
1407 userCount--;
1408
1409 timeq_del(0, chanserv_expire_tempuser, user, TIMEQ_IGNORE_WHEN);
1410 timeq_del(0, chanserv_expire_tempclvl, user, TIMEQ_IGNORE_WHEN);
1411
1412 if(user->prev)
1413 user->prev->next = user->next;
1414 else
1415 channel->users = user->next;
1416 if(user->next)
1417 user->next->prev = user->prev;
1418
1419 if(user->u_prev)
1420 user->u_prev->u_next = user->u_next;
1421 else
1422 user->handle->channels = user->u_next;
1423 if(user->u_next)
1424 user->u_next->u_prev = user->u_prev;
1425
1426 free(user->info);
1427 free(user);
1428 if(do_gc && !channel->users && !IsProtected(channel)) {
1429 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1430 unregister_channel(channel, "lost all users.");
1431 }
1432 }
1433
1434 static struct adduserPending*
1435 add_adduser_pending(struct chanNode *channel, struct userNode *user, int level)
1436 {
1437 struct adduserPending *ap;
1438 ap = calloc(1,sizeof(struct adduserPending));
1439 ap->channel = channel;
1440 ap->user = user;
1441 ap->level = level;
1442 ap->created = time(NULL);
1443
1444 /* ap->prev defaults to NULL already.. */
1445 ap->next = adduser_pendings;
1446 if(adduser_pendings)
1447 adduser_pendings->prev = ap;
1448 adduser_pendings = ap;
1449 adduser_pendings_count++;
1450 return(ap);
1451 }
1452
1453 static void
1454 del_adduser_pending(struct adduserPending *ap)
1455 {
1456 if(ap->prev)
1457 ap->prev->next = ap->next;
1458 else
1459 adduser_pendings = ap->next;
1460
1461 if(ap->next)
1462 ap->next->prev = ap->prev;
1463 free(ap);
1464 }
1465
1466 static void expire_adduser_pending();
1467
1468 /* find_adduser_pending(channel, user) will find an arbitrary record
1469 * from user, channel, or user and channel.
1470 * if user or channel are NULL, they will match any records.
1471 */
1472 static struct adduserPending*
1473 find_adduser_pending(struct chanNode *channel, struct userNode *user)
1474 {
1475 struct adduserPending *ap;
1476
1477 expire_adduser_pending(); /* why not here.. */
1478
1479 if(!channel && !user) /* 2 nulls matches all */
1480 return(adduser_pendings);
1481 for(ap = adduser_pendings;ap;ap = ap->next)
1482 {
1483 if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL))
1484 return ap;
1485 }
1486 return NULL;
1487 }
1488
1489
1490 /* Remove all pendings for a user or channel
1491 *
1492 * called in nickserv.c DelUser() and proto-* unregister_channel()
1493 */
1494 void
1495 wipe_adduser_pending(struct chanNode *channel, struct userNode *user)
1496 {
1497 struct adduserPending *ap;
1498
1499 /* So this is a bit wastefull, i hate dealing with linked lists.
1500 * if its a problem we'll rewrite it right */
1501 while((ap = find_adduser_pending(channel, user))) {
1502 del_adduser_pending(ap);
1503 }
1504 }
1505
1506 /* Called from nickserv.c cmd_auth after someone auths */
1507 void
1508 process_adduser_pending(struct userNode *user)
1509 {
1510 struct adduserPending *ap;
1511 if(!user->handle_info)
1512 return; /* not associated with an account */
1513 while((ap = find_adduser_pending(NULL, user)))
1514 {
1515 struct userData *actee;
1516 if(GetTrueChannelAccess(ap->channel->channel_info, ap->user->handle_info))
1517 {
1518 /* Already on the userlist. do nothing*/
1519 }
1520 else
1521 {
1522 actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL, 0);
1523 scan_user_presence(actee, NULL);
1524 }
1525 del_adduser_pending(ap);
1526 }
1527 }
1528
1529 static void
1530 expire_adduser_pending()
1531 {
1532 struct adduserPending *ap, *ap_next;
1533 ap = adduser_pendings;
1534 while(ap)
1535 {
1536 if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL))
1537 { /* expire it */
1538 ap_next = ap->next; /* save next */
1539 del_adduser_pending(ap); /* free and relink */
1540 ap = ap_next; /* advance */
1541 }
1542 else
1543 ap = ap->next;
1544 }
1545 }
1546
1547 static void expire_ban(void *data);
1548
1549 struct banData*
1550 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1551 {
1552 struct banData *bd;
1553 unsigned int ii, l1, l2;
1554
1555 if(!mask)
1556 return NULL;
1557
1558 bd = malloc(sizeof(struct banData));
1559
1560 bd->channel = channel;
1561 bd->set = set;
1562 bd->triggered = triggered;
1563 bd->expires = expires;
1564
1565 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1566 {
1567 extern const char *hidden_host_suffix;
1568 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1569 char *new_mask;
1570
1571 l1 = strlen(mask);
1572 l2 = strlen(old_name);
1573 if(l2+2 > l1)
1574 continue;
1575 if(irccasecmp(mask + l1 - l2, old_name))
1576 continue;
1577 new_mask = alloca(MAXLEN);
1578 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1579 mask = new_mask;
1580 }
1581 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1582 if(owner)
1583 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1584 bd->reason = strdup(reason);
1585
1586 if(expires)
1587 timeq_add(expires, expire_ban, bd);
1588
1589 bd->prev = NULL;
1590 bd->next = channel->bans; /* lamers */
1591 if(channel->bans)
1592 channel->bans->prev = bd;
1593 channel->bans = bd;
1594 channel->banCount++;
1595 banCount++;
1596
1597 return bd;
1598 }
1599
1600 static void
1601 del_channel_ban(struct banData *ban)
1602 {
1603 ban->channel->banCount--;
1604 banCount--;
1605
1606 if(ban->prev)
1607 ban->prev->next = ban->next;
1608 else
1609 ban->channel->bans = ban->next;
1610
1611 if(ban->next)
1612 ban->next->prev = ban->prev;
1613
1614 if(ban->expires)
1615 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1616
1617 if(ban->reason)
1618 free(ban->reason);
1619
1620 free(ban);
1621 }
1622
1623 static void
1624 expire_ban(void *data) /* lamer.. */
1625 {
1626 struct banData *bd = data;
1627 if(!IsSuspended(bd->channel))
1628 {
1629 struct banList bans;
1630 struct mod_chanmode change;
1631 unsigned int ii;
1632 bans = bd->channel->channel->banlist;
1633 mod_chanmode_init(&change);
1634 for(ii=0; ii<bans.used; ii++)
1635 {
1636 if(!strcmp(bans.list[ii]->ban, bd->mask))
1637 {
1638 change.argc = 1;
1639 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1640 change.args[0].u.hostmask = bd->mask;
1641 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1642 break;
1643 }
1644 }
1645 }
1646 bd->expires = 0;
1647 del_channel_ban(bd);
1648 }
1649
1650 static void chanserv_expire_suspension(void *data);
1651
1652 static void
1653 unregister_channel(struct chanData *channel, const char *reason)
1654 {
1655 struct mod_chanmode change;
1656 char msgbuf[MAXLEN];
1657
1658 /* After channel unregistration, the following must be cleaned
1659 up:
1660 - Channel information.
1661 - Channel users.
1662 - Channel bans. (lamers)
1663 - Channel suspension data.
1664 - adduser_pending data.
1665 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1666 */
1667
1668 if(!channel)
1669 return;
1670
1671 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1672
1673 if(off_channel > 0)
1674 {
1675 mod_chanmode_init(&change);
1676 change.modes_clear |= MODE_REGISTERED;
1677 mod_chanmode_announce(chanserv, channel->channel, &change);
1678 }
1679
1680 wipe_adduser_pending(channel->channel, NULL);
1681
1682 while(channel->users)
1683 del_channel_user(channel->users, 0);
1684
1685 while(channel->bans)
1686 del_channel_ban(channel->bans);
1687
1688 free(channel->topic);
1689 free(channel->registrar);
1690 free(channel->greeting);
1691 free(channel->user_greeting);
1692 free(channel->topic_mask);
1693
1694 if(channel->prev)
1695 channel->prev->next = channel->next;
1696 else
1697 channelList = channel->next;
1698
1699 if(channel->next)
1700 channel->next->prev = channel->prev;
1701
1702 if(channel->suspended)
1703 {
1704 struct chanNode *cNode = channel->channel;
1705 struct suspended *suspended, *next_suspended;
1706
1707 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1708 {
1709 next_suspended = suspended->previous;
1710 free(suspended->suspender);
1711 free(suspended->reason);
1712 if(suspended->expires)
1713 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1714 free(suspended);
1715 }
1716
1717 if(cNode)
1718 cNode->channel_info = NULL;
1719 }
1720 channel->channel->channel_info = NULL;
1721
1722 dict_delete(channel->notes);
1723 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1724 if(!IsSuspended(channel))
1725 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1726 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1727 UnlockChannel(channel->channel);
1728 free(channel);
1729 registered_channels--;
1730 }
1731
1732 static void
1733 expire_channels(UNUSED_ARG(void *data))
1734 {
1735 struct chanData *channel, *next;
1736 struct userData *user;
1737 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1738
1739 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1740 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1741
1742 for(channel = channelList; channel; channel = next)
1743 {
1744 next = channel->next;
1745
1746 /* See if the channel can be expired. */
1747 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1748 || IsProtected(channel))
1749 continue;
1750
1751 /* Make sure there are no high-ranking users still in the channel. */
1752 for(user=channel->users; user; user=user->next)
1753 if(user->present && (user->access >= UL_PRESENT))
1754 break;
1755 if(user)
1756 continue;
1757
1758 /* Unregister the channel */
1759 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1760 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1761 unregister_channel(channel, "registration expired.");
1762 }
1763
1764 if(chanserv_conf.channel_expire_frequency)
1765 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1766 }
1767
1768 static void
1769 expire_dnrs(UNUSED_ARG(void *data))
1770 {
1771 dict_iterator_t it;
1772 struct do_not_register *dnr;
1773
1774 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1775 {
1776 dnr = iter_data(it);
1777 if(!dnr->expires || dnr->expires > now)
1778 continue;
1779 dict_remove(handle_dnrs, dnr->chan_name + 1);
1780 }
1781 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1782 {
1783 dnr = iter_data(it);
1784 if(!dnr->expires || dnr->expires > now)
1785 continue;
1786 dict_remove(plain_dnrs, dnr->chan_name);
1787 }
1788 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1789 {
1790 dnr = iter_data(it);
1791 if(!dnr->expires || dnr->expires > now)
1792 continue;
1793 dict_remove(mask_dnrs, dnr->chan_name);
1794 }
1795
1796 if(chanserv_conf.dnr_expire_frequency)
1797 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1798 }
1799
1800 static int
1801 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel, int protect_invitables)
1802 {
1803 char protect = channel->chOpts[chProtect];
1804 struct userData *cs_victim, *cs_aggressor;
1805
1806 /* If victim access level is greater than set invitelevel, don't let
1807 * us kick them, but don't consider it punishment if someone else does
1808 */
1809
1810
1811 if(victim == aggressor)
1812 return 0;
1813 /* Don't protect if the victim isn't authenticated (because they
1814 can't be a channel user), unless we are to protect non-users
1815 also. */
1816
1817 cs_victim = GetChannelAccess(channel, victim->handle_info);
1818
1819 /* If they have enough access to invite themselvs through a ban,
1820 * and its us kicking them, don't. -Rubin */
1821 if(protect_invitables==true && cs_victim && (cs_victim->access >= channel->lvlOpts[lvlInviteMe]))
1822 return 1;
1823
1824 if(protect == 'n')
1825 return 0;
1826
1827 if(protect != 'a' && !cs_victim)
1828 return 0;
1829
1830 /* Protect if the aggressor isn't a user because at this point,
1831 the aggressor can only be less than or equal to the victim. */
1832
1833 /* Not protected from chanserv except above */
1834 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1835 if(aggressor == chanserv)
1836 return 0;
1837
1838 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1839 if(!cs_aggressor)
1840 return 1;
1841
1842 /* If the aggressor was a user, then the victim can't be helped. */
1843 if(!cs_victim)
1844 return 0;
1845
1846 switch(protect)
1847 {
1848 case 'l':
1849 if(cs_victim->access > cs_aggressor->access)
1850 return 1;
1851 break;
1852 case 'a':
1853 case 'e':
1854 if(cs_victim->access >= cs_aggressor->access)
1855 return 1;
1856 break;
1857 }
1858
1859 return 0;
1860 }
1861
1862 static int
1863 validate_op(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1864 {
1865 struct chanData *cData = channel->channel_info;
1866 struct userData *cs_victim;
1867
1868 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1869 || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/))
1870 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1871 {
1872 if(cmd)
1873 reply("CSMSG_OPBY_LOCKED");
1874 else
1875 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1876 return 0;
1877 }
1878
1879 return 1;
1880 }
1881
1882 static int
1883 validate_halfop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1884 {
1885 struct chanData *cData = channel->channel_info;
1886 struct userData *cs_victim;
1887
1888 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1889 || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */))
1890 && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0))
1891 {
1892 reply("CSMSG_HOPBY_LOCKED");
1893 return 0;
1894 }
1895
1896 return 1;
1897 }
1898
1899
1900 static int
1901 validate_deop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1902 {
1903 if(IsService(victim))
1904 {
1905 reply("MSG_SERVICE_IMMUNE", victim->nick);
1906 return 0;
1907 }
1908
1909 if(protect_user(victim, user, channel->channel_info, false))
1910 {
1911 reply("CSMSG_USER_PROTECTED", victim->nick);
1912 return 0;
1913 }
1914
1915 return 1;
1916 }
1917
1918 static int
1919 validate_dehop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1920 {
1921 if(IsService(victim))
1922 {
1923 reply("MSG_SERVICE_IMMUNE", victim->nick);
1924 return 0;
1925 }
1926
1927 if(protect_user(victim, user, channel->channel_info, false))
1928 {
1929 reply("CSMSG_USER_PROTECTED", victim->nick);
1930 return 0;
1931 }
1932
1933 return 1;
1934 }
1935
1936 static struct do_not_register *
1937 chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
1938 {
1939 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1940 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1941 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1942 strcpy(dnr->reason, reason);
1943 dnr->set = now;
1944 dnr->expires = expires;
1945 if(dnr->chan_name[0] == '*')
1946 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1947 else if(strpbrk(dnr->chan_name, "*?"))
1948 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1949 else
1950 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1951 return dnr;
1952 }
1953
1954 static struct dnrList
1955 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1956 {
1957 struct dnrList list;
1958 dict_iterator_t it, next;
1959 struct do_not_register *dnr;
1960
1961 dnrList_init(&list);
1962
1963 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1964 {
1965 if(dnr->expires && dnr->expires <= now)
1966 dict_remove(handle_dnrs, handle);
1967 else if(list.used < max)
1968 dnrList_append(&list, dnr);
1969 }
1970
1971 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1972 {
1973 if(dnr->expires && dnr->expires <= now)
1974 dict_remove(plain_dnrs, chan_name);
1975 else if(list.used < max)
1976 dnrList_append(&list, dnr);
1977 }
1978
1979 if(chan_name)
1980 {
1981 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1982 {
1983 next = iter_next(it);
1984 if(!match_ircglob(chan_name, iter_key(it)))
1985 continue;
1986 dnr = iter_data(it);
1987 if(dnr->expires && dnr->expires <= now)
1988 dict_remove(mask_dnrs, iter_key(it));
1989 else
1990 dnrList_append(&list, dnr);
1991 }
1992 }
1993
1994 return list;
1995 }
1996
1997 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1998 {
1999 struct userNode *user;
2000 char buf1[INTERVALLEN];
2001 char buf2[INTERVALLEN];
2002
2003 user = extra;
2004 if(dnr->set)
2005 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
2006 if(dnr->expires)
2007 {
2008 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
2009 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
2010 }
2011 else if(dnr->set)
2012 {
2013 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
2014 }
2015 else
2016 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
2017 return 0;
2018 }
2019
2020 static unsigned int
2021 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
2022 {
2023 struct dnrList list;
2024 unsigned int ii;
2025
2026 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
2027 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
2028 dnr_print_func(list.list[ii], user);
2029
2030 if(ii < list.used)
2031 reply("CSMSG_MORE_DNRS", list.used - ii);
2032 free(list.list);
2033 return ii;
2034 }
2035
2036 struct do_not_register *
2037 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
2038 {
2039 struct dnrList list;
2040 struct do_not_register *dnr;
2041
2042 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
2043 dnr = list.used ? list.list[0] : NULL;
2044 free(list.list);
2045
2046 return dnr;
2047 }
2048
2049 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
2050 {
2051 struct do_not_register *dnr;
2052 dict_iterator_t it, next;
2053 unsigned int matches = 0;
2054
2055 for(it = dict_first(dict); it; it = next)
2056 {
2057 dnr = iter_data(it);
2058 next = iter_next(it);
2059 if(dnr->expires && dnr->expires <= now)
2060 {
2061 dict_remove(dict, iter_key(it));
2062 continue;
2063 }
2064 dnr_print_func(dnr, user);
2065 matches++;
2066 }
2067
2068 return matches;
2069 }
2070
2071 static CHANSERV_FUNC(cmd_noregister)
2072 {
2073 const char *target;
2074 time_t expiry, duration;
2075 unsigned int matches;
2076
2077 if(argc < 2)
2078 {
2079 reply("CSMSG_DNR_SEARCH_RESULTS");
2080 matches = send_dnrs(user, handle_dnrs);
2081 matches += send_dnrs(user, plain_dnrs);
2082 matches += send_dnrs(user, mask_dnrs);
2083 if(matches)
2084 reply("MSG_MATCH_COUNT", matches);
2085 else
2086 reply("MSG_NO_MATCHES");
2087 return 0;
2088 }
2089
2090 target = argv[1];
2091
2092 if(!IsChannelName(target) && (*target != '*'))
2093 {
2094 reply("CSMSG_NOT_DNR", target);
2095 return 0;
2096 }
2097
2098 if(argc > 2)
2099 {
2100 if(argc == 3)
2101 {
2102 reply("MSG_INVALID_DURATION", argv[2]);
2103 return 0;
2104 }
2105
2106 if(!strcmp(argv[2], "0"))
2107 expiry = 0;
2108 else if((duration = ParseInterval(argv[2])))
2109 expiry = now + duration;
2110 else
2111 {
2112 reply("MSG_INVALID_DURATION", argv[2]);
2113 return 0;
2114 }
2115
2116 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
2117 if((*target == '*') && !get_handle_info(target + 1))
2118 {
2119 reply("MSG_HANDLE_UNKNOWN", target + 1);
2120 return 0;
2121 }
2122 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
2123 reply("CSMSG_NOREGISTER_CHANNEL", target);
2124 return 1;
2125 }
2126
2127 reply("CSMSG_DNR_SEARCH_RESULTS");
2128 if(*target == '*')
2129 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
2130 else
2131 matches = chanserv_show_dnrs(user, cmd, target, NULL);
2132 if(!matches)
2133 reply("MSG_NO_MATCHES");
2134 return 0;
2135 }
2136
2137 static CHANSERV_FUNC(cmd_allowregister)
2138 {
2139 const char *chan_name = argv[1];
2140
2141 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2142 || dict_remove(plain_dnrs, chan_name)
2143 || dict_remove(mask_dnrs, chan_name))
2144 {
2145 reply("CSMSG_DNR_REMOVED", chan_name);
2146 return 1;
2147 }
2148 reply("CSMSG_NO_SUCH_DNR", chan_name);
2149 return 0;
2150 }
2151
2152 struct dnr_search {
2153 struct userNode *source;
2154 char *chan_mask;
2155 char *setter_mask;
2156 char *reason_mask;
2157 time_t min_set, max_set;
2158 time_t min_expires, max_expires;
2159 unsigned int limit;
2160 };
2161
2162 static int
2163 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
2164 {
2165 return !((dnr->set < search->min_set)
2166 || (dnr->set > search->max_set)
2167 || (dnr->expires && ((dnr->expires < search->min_expires)
2168 || (dnr->expires > search->max_expires)))
2169 || (search->chan_mask
2170 && !match_ircglob(dnr->chan_name, search->chan_mask))
2171 || (search->setter_mask
2172 && !match_ircglob(dnr->setter, search->setter_mask))
2173 || (search->reason_mask
2174 && !match_ircglob(dnr->reason, search->reason_mask)));
2175 }
2176
2177 static struct dnr_search *
2178 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
2179 {
2180 struct dnr_search *discrim;
2181 unsigned int ii;
2182
2183 discrim = calloc(1, sizeof(*discrim));
2184 discrim->source = user;
2185 discrim->chan_mask = NULL;
2186 discrim->setter_mask = NULL;
2187 discrim->reason_mask = NULL;
2188 discrim->max_set = INT_MAX;
2189 discrim->max_expires = INT_MAX;
2190 discrim->limit = 50;
2191
2192 for(ii=0; ii<argc; ++ii)
2193 {
2194 if(ii == argc - 1)
2195 {
2196 reply("MSG_MISSING_PARAMS", argv[ii]);
2197 goto fail;
2198 }
2199 else if(0 == irccasecmp(argv[ii], "channel"))
2200 {
2201 discrim->chan_mask = argv[++ii];
2202 }
2203 else if(0 == irccasecmp(argv[ii], "setter"))
2204 {
2205 discrim->setter_mask = argv[++ii];
2206 }
2207 else if(0 == irccasecmp(argv[ii], "reason"))
2208 {
2209 discrim->reason_mask = argv[++ii];
2210 }
2211 else if(0 == irccasecmp(argv[ii], "limit"))
2212 {
2213 discrim->limit = strtoul(argv[++ii], NULL, 0);
2214 }
2215 else if(0 == irccasecmp(argv[ii], "set"))
2216 {
2217 const char *cmp = argv[++ii];
2218 if(cmp[0] == '<') {
2219 if(cmp[1] == '=')
2220 discrim->min_set = now - ParseInterval(cmp + 2);
2221 else
2222 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
2223 } else if(cmp[0] == '=') {
2224 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
2225 } else if(cmp[0] == '>') {
2226 if(cmp[1] == '=')
2227 discrim->max_set = now - ParseInterval(cmp + 2);
2228 else
2229 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
2230 } else {
2231 discrim->max_set = now - (ParseInterval(cmp) - 1);
2232 }
2233 }
2234 else if(0 == irccasecmp(argv[ii], "expires"))
2235 {
2236 const char *cmp = argv[++ii];
2237 if(cmp[0] == '<') {
2238 if(cmp[1] == '=')
2239 discrim->max_expires = now + ParseInterval(cmp + 2);
2240 else
2241 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
2242 } else if(cmp[0] == '=') {
2243 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
2244 } else if(cmp[0] == '>') {
2245 if(cmp[1] == '=')
2246 discrim->min_expires = now + ParseInterval(cmp + 2);
2247 else
2248 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2249 } else {
2250 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2251 }
2252 }
2253 else
2254 {
2255 reply("MSG_INVALID_CRITERIA", argv[ii]);
2256 goto fail;
2257 }
2258 }
2259 return discrim;
2260
2261 fail:
2262 free(discrim);
2263 return NULL;
2264 }
2265
2266 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2267
2268 static unsigned int
2269 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2270 {
2271 struct do_not_register *dnr;
2272 dict_iterator_t next;
2273 dict_iterator_t it;
2274 unsigned int count;
2275 int target_fixed;
2276
2277 /* Initialize local variables. */
2278 count = 0;
2279 target_fixed = 0;
2280 if(discrim->chan_mask)
2281 {
2282 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2283 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2284 target_fixed = 1;
2285 }
2286
2287 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2288 {
2289 /* Check against account-based DNRs. */
2290 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2291 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2292 dsf(dnr, data);
2293 }
2294 else if(target_fixed)
2295 {
2296 /* Check against channel-based DNRs. */
2297 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2298 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2299 dsf(dnr, data);
2300 }
2301 else
2302 {
2303 /* Exhaustively search account DNRs. */
2304 for(it = dict_first(handle_dnrs); it; it = next)
2305 {
2306 next = iter_next(it);
2307 dnr = iter_data(it);
2308 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2309 break;
2310 }
2311 /* Do the same for channel DNRs. */
2312 for(it = dict_first(plain_dnrs); it; it = next)
2313 {
2314 next = iter_next(it);
2315 dnr = iter_data(it);
2316 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2317 break;
2318 }
2319
2320 /* Do the same for wildcarded channel DNRs. */
2321 for(it = dict_first(mask_dnrs); it; it = next)
2322 {
2323 next = iter_next(it);
2324 dnr = iter_data(it);
2325 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2326 break;
2327 }
2328 }
2329 return count;
2330 }
2331
2332 static int
2333 dnr_remove_func(struct do_not_register *match, void *extra)
2334 {
2335 struct userNode *user;
2336 char *chan_name;
2337
2338 chan_name = alloca(strlen(match->chan_name) + 1);
2339 strcpy(chan_name, match->chan_name);
2340 user = extra;
2341 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2342 || dict_remove(plain_dnrs, chan_name)
2343 || dict_remove(mask_dnrs, chan_name))
2344 {
2345 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2346 }
2347 return 0;
2348 }
2349
2350 static int
2351 dnr_count_func(struct do_not_register *match, void *extra)
2352 {
2353 return 0; (void)match; (void)extra;
2354 }
2355
2356 static MODCMD_FUNC(cmd_dnrsearch)
2357 {
2358 struct dnr_search *discrim;
2359 dnr_search_func action;
2360 struct svccmd *subcmd;
2361 unsigned int matches;
2362 char buf[MAXLEN];
2363
2364 sprintf(buf, "dnrsearch %s", argv[1]);
2365 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2366 if(!subcmd)
2367 {
2368 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2369 return 0;
2370 }
2371 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2372 return 0;
2373 if(!irccasecmp(argv[1], "print"))
2374 action = dnr_print_func;
2375 else if(!irccasecmp(argv[1], "remove"))
2376 action = dnr_remove_func;
2377 else if(!irccasecmp(argv[1], "count"))
2378 action = dnr_count_func;
2379 else
2380 {
2381 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2382 return 0;
2383 }
2384
2385 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2386 if(!discrim)
2387 return 0;
2388
2389 if(action == dnr_print_func)
2390 reply("CSMSG_DNR_SEARCH_RESULTS");
2391 matches = dnr_search(discrim, action, user);
2392 if(matches)
2393 reply("MSG_MATCH_COUNT", matches);
2394 else
2395 reply("MSG_NO_MATCHES");
2396 free(discrim);
2397 return 1;
2398 }
2399
2400 unsigned int
2401 chanserv_get_owned_count(struct handle_info *hi)
2402 {
2403 struct userData *cList;
2404 unsigned int owned;
2405
2406 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2407 if(cList->access == UL_OWNER)
2408 owned++;
2409 return owned;
2410 }
2411
2412 static CHANSERV_FUNC(cmd_register)
2413 {
2414 struct handle_info *handle;
2415 struct chanData *cData;
2416 struct modeNode *mn;
2417 char reason[MAXLEN];
2418 char *chan_name;
2419 unsigned int new_channel, force=0;
2420 struct do_not_register *dnr;
2421 unsigned int n;
2422
2423 if (checkDefCon(DEFCON_NO_NEW_CHANNELS) && !IsOper(user)) {
2424 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2425 return 0;
2426 }
2427
2428 if(channel)
2429 {
2430 if(channel->channel_info)
2431 {
2432 reply("CSMSG_ALREADY_REGGED", channel->name);
2433 return 0;
2434 }
2435
2436 if(channel->bad_channel)
2437 {
2438 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2439 return 0;
2440 }
2441
2442 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2443 {
2444 reply("CSMSG_MUST_BE_OPPED", channel->name);
2445 return 0;
2446 }
2447
2448 new_channel = 0;
2449 chan_name = channel->name;
2450 }
2451 else
2452 {
2453 if(argc < 2)
2454 {
2455 reply("MSG_MISSING_PARAMS", cmd->name);
2456 svccmd_send_help_brief(user, chanserv, cmd);
2457 return 0;
2458 }
2459 if(!IsChannelName(argv[1]))
2460 {
2461 reply("MSG_NOT_CHANNEL_NAME");
2462 return 0;
2463 }
2464
2465 if(opserv_bad_channel(argv[1]))
2466 {
2467 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2468 return 0;
2469 }
2470
2471 new_channel = 1;
2472 chan_name = argv[1];
2473 }
2474
2475 if(argc >= (new_channel+2))
2476 {
2477 if(!IsHelping(user))
2478 {
2479 reply("CSMSG_PROXY_FORBIDDEN");
2480 return 0;
2481 }
2482
2483 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2484 return 0;
2485 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2486 dnr = chanserv_is_dnr(chan_name, handle);
2487
2488 /* Check if they are over the limit.. */
2489 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2490 {
2491 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2492 return 0;
2493 }
2494
2495 }
2496 else
2497 {
2498 handle = user->handle_info;
2499 dnr = chanserv_is_dnr(chan_name, handle);
2500 /* Check if they are over the limit.. */
2501 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2502 {
2503 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned);
2504 return 0;
2505 }
2506 /* Check if another service is in the channel */
2507 if(channel)
2508 for(n = 0; n < channel->members.used; n++)
2509 {
2510 mn = channel->members.list[n];
2511 if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user))
2512 {
2513 reply("CSMSG_ANOTHER_SERVICE");
2514 return 0;
2515 }
2516 }
2517 }
2518 if(dnr && !force)
2519 {
2520 if(!IsHelping(user))
2521 reply("CSMSG_DNR_CHANNEL", chan_name);
2522 else
2523 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2524 return 0;
2525 }
2526
2527 /* now handled above for message specilization *
2528 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2529 {
2530 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2531 return 0;
2532 }
2533 */
2534
2535 if(new_channel)
2536 channel = AddChannel(argv[1], now, NULL, NULL, NULL);
2537
2538 cData = register_channel(channel, user->handle_info->handle);
2539 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL, 0), NULL);
2540 cData->modes = chanserv_conf.default_modes;
2541 if(off_channel > 0)
2542 cData->modes.modes_set |= MODE_REGISTERED;
2543 if (IsOffChannel(cData))
2544 {
2545 mod_chanmode_announce(chanserv, channel, &cData->modes);
2546 }
2547 else
2548 {
2549 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2550 change->args[change->argc].mode = MODE_CHANOP;
2551 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2552 change->argc++;
2553 mod_chanmode_announce(chanserv, channel, change);
2554 mod_chanmode_free(change);
2555 }
2556
2557 /* Initialize the channel's max user record. */
2558 cData->max = channel->members.used;
2559
2560 if(handle != user->handle_info)
2561 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2562 else
2563
2564 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2565 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_REGISTERED_TO", channel->name,
2566 handle->handle, user->handle_info->handle);
2567 return 1;
2568 }
2569
2570 static const char *
2571 make_confirmation_string(struct userData *uData)
2572 {
2573 static char strbuf[16];
2574 char *src;
2575 unsigned int accum;
2576
2577 accum = 0;
2578 for(src = uData->handle->handle; *src; )
2579 accum = accum * 31 + toupper(*src++);
2580 if(uData->channel)
2581 for(src = uData->channel->channel->name; *src; )
2582 accum = accum * 31 + toupper(*src++);
2583 sprintf(strbuf, "%08x", accum);
2584 return strbuf;
2585 }
2586
2587 static CHANSERV_FUNC(cmd_unregister)
2588 {
2589 char *name;
2590 char reason[MAXLEN];
2591 struct chanData *cData;
2592 struct userData *uData;
2593
2594 cData = channel->channel_info;
2595 if(!cData)
2596 {
2597 reply("CSMSG_NOT_REGISTERED", channel->name);
2598 return 0;
2599 }
2600
2601 uData = GetChannelUser(cData, user->handle_info);
2602 if(!uData || (uData->access < UL_OWNER))
2603 {
2604 reply("CSMSG_NO_ACCESS");
2605 return 0;
2606 }
2607
2608 if(IsProtected(cData))
2609 {
2610 reply("CSMSG_UNREG_NODELETE", channel->name);
2611 return 0;
2612 }
2613
2614 if(!IsHelping(user))
2615 {
2616 const char *confirm_string;
2617 if(IsSuspended(cData))
2618 {
2619 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2620 return 0;
2621 }
2622 confirm_string = make_confirmation_string(uData);
2623 if((argc < 2) || strcmp(argv[1], confirm_string))
2624 {
2625 reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
2626 return 0;
2627 }
2628 }
2629
2630 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2631 name = strdup(channel->name);
2632 unregister_channel(cData, reason);
2633 spamserv_cs_unregister(user, channel, manually, "unregistered");
2634 reply("CSMSG_UNREG_SUCCESS", name);
2635 free(name);
2636 return 1;
2637 }
2638
2639 static void
2640 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2641 {
2642 extern struct userNode *spamserv;
2643 struct mod_chanmode *change;
2644
2645 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2646 {
2647 change = mod_chanmode_alloc(2);
2648 change->argc = 2;
2649 change->args[0].mode = MODE_CHANOP;
2650 change->args[0].u.member = AddChannelUser(chanserv, channel);
2651 change->args[1].mode = MODE_CHANOP;
2652 change->args[1].u.member = AddChannelUser(spamserv, channel);
2653 }
2654 else
2655 {
2656 change = mod_chanmode_alloc(1);
2657 change->argc = 1;
2658 change->args[0].mode = MODE_CHANOP;
2659 change->args[0].u.member = AddChannelUser(chanserv, channel);
2660 }
2661
2662 mod_chanmode_announce(chanserv, channel, change);
2663 mod_chanmode_free(change);
2664 }
2665
2666 static CHANSERV_FUNC(cmd_move)
2667 {
2668 struct mod_chanmode change;
2669 struct chanNode *target;
2670 struct modeNode *mn;
2671 struct userData *uData;
2672 struct do_not_register *dnr;
2673 int chanserv_join = 0, spamserv_join;
2674
2675 REQUIRE_PARAMS(2);
2676
2677 if(IsProtected(channel->channel_info))
2678 {
2679 reply("CSMSG_MOVE_NODELETE", channel->name);
2680 return 0;
2681 }
2682
2683 if(!IsChannelName(argv[1]))
2684 {
2685 reply("MSG_NOT_CHANNEL_NAME");
2686 return 0;
2687 }
2688
2689 if(opserv_bad_channel(argv[1]))
2690 {
2691 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2692 return 0;
2693 }
2694
2695 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2696 {
2697 for(uData = channel->channel_info->users; uData; uData = uData->next)
2698 {
2699 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2700 {
2701 if(!IsHelping(user))
2702 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2703 else
2704 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2705 return 0;
2706 }
2707 }
2708 }
2709
2710 mod_chanmode_init(&change);
2711 if(!(target = GetChannel(argv[1])))
2712 {
2713 target = AddChannel(argv[1], now, NULL, NULL, NULL);
2714 if(!IsSuspended(channel->channel_info))
2715 chanserv_join = 1;
2716 }
2717 else if(target->channel_info)
2718 {
2719 reply("CSMSG_ALREADY_REGGED", target->name);
2720 return 0;
2721 }
2722 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2723 && !IsHelping(user))
2724 {
2725 reply("CSMSG_MUST_BE_OPPED", target->name);
2726 return 0;
2727 }
2728 else if(!IsSuspended(channel->channel_info))
2729 chanserv_join = 1;
2730
2731 if(off_channel > 0)
2732 {
2733 /* Clear MODE_REGISTERED from old channel, add it to new. */
2734 change.argc = 0;
2735 change.modes_clear = MODE_REGISTERED;
2736 mod_chanmode_announce(chanserv, channel, &change);
2737 change.modes_clear = 0;
2738 change.modes_set = MODE_REGISTERED;
2739 mod_chanmode_announce(chanserv, target, &change);
2740 }
2741
2742 /* Move the channel_info to the target channel; it
2743 shouldn't be necessary to clear timeq callbacks
2744 for the old channel. */
2745 target->channel_info = channel->channel_info;
2746 target->channel_info->channel = target;
2747 channel->channel_info = NULL;
2748
2749 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2750
2751 if (chanserv_join)
2752 ss_cs_join_channel(target, spamserv_join);
2753
2754 if(!IsSuspended(target->channel_info))
2755 {
2756 char reason2[MAXLEN];
2757 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2758 DelChannelUser(chanserv, channel, reason2, 0);
2759 }
2760
2761 UnlockChannel(channel);
2762 LockChannel(target);
2763 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_CHANNEL_MOVED",
2764 channel->name, target->name, user->handle_info->handle);
2765
2766 reply("CSMSG_MOVE_SUCCESS", target->name);
2767 return 1;
2768 }
2769
2770 static void
2771 merge_users(struct chanData *source, struct chanData *target)
2772 {
2773 struct userData *suData, *tuData, *next;
2774 dict_iterator_t it;
2775 dict_t merge;
2776
2777 merge = dict_new();
2778
2779 /* Insert the source's users into the scratch area. */
2780 for(suData = source->users; suData; suData = suData->next)
2781 dict_insert(merge, suData->handle->handle, suData);
2782
2783 /* Iterate through the target's users, looking for
2784 users common to both channels. The lower access is
2785 removed from either the scratch area or target user
2786 list. */
2787 for(tuData = target->users; tuData; tuData = next)
2788 {
2789 struct userData *choice;
2790
2791 next = tuData->next;
2792
2793 /* If a source user exists with the same handle as a target
2794 channel's user, resolve the conflict by removing one. */
2795 suData = dict_find(merge, tuData->handle->handle, NULL);
2796 if(!suData)
2797 continue;
2798
2799 /* Pick the data we want to keep. */
2800 /* If the access is the same, use the later seen time. */
2801 if(suData->access == tuData->access)
2802 choice = (suData->seen > tuData->seen) ? suData : tuData;
2803 else /* Otherwise, keep the higher access level. */
2804 choice = (suData->access > tuData->access) ? suData : tuData;
2805
2806 /* Remove the user that wasn't picked. */
2807 if(choice == tuData)
2808 {
2809 dict_remove(merge, suData->handle->handle);
2810 del_channel_user(suData, 0);
2811 }
2812 else
2813 del_channel_user(tuData, 0);
2814 }
2815
2816 /* Move the remaining users to the target channel. */
2817 for(it = dict_first(merge); it; it = iter_next(it))
2818 {
2819 suData = iter_data(it);
2820
2821 /* Insert the user into the target channel's linked list. */
2822 suData->prev = NULL;
2823 suData->next = target->users;
2824 suData->channel = target;
2825
2826 if(target->users)
2827 target->users->prev = suData;
2828 target->users = suData;
2829
2830 /* Update the user counts for the target channel; the
2831 source counts are left alone. */
2832 target->userCount++;
2833 }
2834
2835 /* Possible to assert (source->users == NULL) here. */
2836 source->users = NULL;
2837 dict_delete(merge);
2838 }
2839
2840 static void
2841 merge_bans(struct chanData *source, struct chanData *target)
2842 {
2843 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2844
2845 /* Hold on to the original head of the target ban list
2846 to avoid comparing source bans with source bans. */
2847 tFront = target->bans;
2848
2849 /* Perform a totally expensive O(n*m) merge, ick. */
2850 for(sbData = source->bans; sbData; sbData = sNext)
2851 {
2852 /* Flag to track whether the ban's been moved
2853 to the destination yet. */
2854 int moved = 0;
2855
2856 /* Possible to assert (sbData->prev == NULL) here. */
2857 sNext = sbData->next;
2858
2859 for(tbData = tFront; tbData; tbData = tNext)
2860 {
2861 tNext = tbData->next;
2862
2863 /* Perform two comparisons between each source
2864 and target ban, conflicts are resolved by
2865 keeping the broader ban and copying the later
2866 expiration and triggered time. */
2867 if(match_ircglobs(tbData->mask, sbData->mask))
2868 {
2869 /* There is a broader ban in the target channel that
2870 overrides one in the source channel; remove the
2871 source ban and break. */
2872 if(sbData->expires > tbData->expires)
2873 tbData->expires = sbData->expires;
2874 if(sbData->triggered > tbData->triggered)
2875 tbData->triggered = sbData->triggered;
2876 del_channel_ban(sbData);
2877 break;
2878 }
2879 else if(match_ircglobs(sbData->mask, tbData->mask))
2880 {
2881 /* There is a broader ban in the source channel that
2882 overrides one in the target channel; remove the
2883 target ban, fall through and move the source over. */
2884 if(tbData->expires > sbData->expires)
2885 sbData->expires = tbData->expires;
2886 if(tbData->triggered > sbData->triggered)
2887 sbData->triggered = tbData->triggered;
2888 if(tbData == tFront)
2889 tFront = tNext;
2890 del_channel_ban(tbData);
2891 }
2892
2893 /* Source bans can override multiple target bans, so
2894 we allow a source to run through this loop multiple
2895 times, but we can only move it once. */
2896 if(moved)
2897 continue;
2898 moved = 1;
2899
2900 /* Remove the source ban from the source ban list. */
2901 if(sbData->next)
2902 sbData->next->prev = sbData->prev;
2903
2904 /* Modify the source ban's associated channel. */
2905 sbData->channel = target;
2906
2907 /* Insert the ban into the target channel's linked list. */
2908 sbData->prev = NULL;
2909 sbData->next = target->bans;
2910
2911 if(target->bans)
2912 target->bans->prev = sbData;
2913 target->bans = sbData;
2914
2915 /* Update the user counts for the target channel. */
2916 target->banCount++;
2917 }
2918 }
2919
2920 /* Possible to assert (source->bans == NULL) here. */
2921 source->bans = NULL;
2922 }
2923
2924 static void
2925 merge_data(struct chanData *source, struct chanData *target)
2926 {
2927 /* Use more recent visited and owner-transfer time; use older
2928 * registered time. Bitwise or may_opchan. Use higher max.
2929 * Do not touch last_refresh, ban count or user counts.
2930 */
2931 if(source->visited > target->visited)
2932 target->visited = source->visited;
2933 if(source->registered < target->registered)
2934 target->registered = source->registered;
2935 if(source->ownerTransfer > target->ownerTransfer)
2936 target->ownerTransfer = source->ownerTransfer;
2937 if(source->may_opchan)
2938 target->may_opchan = 1;
2939 if(source->max > target->max)
2940 target->max = source->max;
2941 }
2942
2943 static void
2944 merge_channel(struct chanData *source, struct chanData *target)
2945 {
2946 merge_users(source, target);
2947 merge_bans(source, target);
2948 merge_data(source, target);
2949 }
2950
2951 static CHANSERV_FUNC(cmd_merge)
2952 {
2953 struct userData *target_user;
2954 struct chanNode *target;
2955 char reason[MAXLEN];
2956
2957 REQUIRE_PARAMS(2);
2958
2959 /* Make sure the target channel exists and is registered to the user
2960 performing the command. */
2961 if(!(target = GetChannel(argv[1])))
2962 {
2963 reply("MSG_INVALID_CHANNEL");
2964 return 0;
2965 }
2966
2967 if(!target->channel_info)
2968 {
2969 reply("CSMSG_NOT_REGISTERED", target->name);
2970 return 0;
2971 }
2972
2973 if(IsProtected(channel->channel_info))
2974 {
2975 reply("CSMSG_MERGE_NODELETE");
2976 return 0;
2977 }
2978
2979 if(IsSuspended(target->channel_info))
2980 {
2981 reply("CSMSG_MERGE_SUSPENDED");
2982 return 0;
2983 }
2984
2985 if(channel == target)
2986 {
2987 reply("CSMSG_MERGE_SELF");
2988 return 0;
2989 }
2990
2991 target_user = GetChannelUser(target->channel_info, user->handle_info);
2992 if(!target_user || (target_user->access < UL_OWNER))
2993 {
2994 reply("CSMSG_MERGE_NOT_OWNER");
2995 return 0;
2996 }
2997
2998 /* Merge the channel structures and associated data. */
2999 merge_channel(channel->channel_info, target->channel_info);
3000 spamserv_cs_move_merge(user, channel, target, 0);
3001 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
3002 unregister_channel(channel->channel_info, reason);
3003 reply("CSMSG_MERGE_SUCCESS", target->name);
3004 return 1;
3005 }
3006
3007 static CHANSERV_FUNC(cmd_opchan)
3008 {
3009 struct mod_chanmode change;
3010 if(!IsHelping(user) && !channel->channel_info->may_opchan)
3011 {
3012 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
3013 return 0;
3014 }
3015 channel->channel_info->may_opchan = 0;
3016 mod_chanmode_init(&change);
3017 change.argc = 1;
3018 change.args[0].mode = MODE_CHANOP;
3019 change.args[0].u.member = GetUserMode(channel, chanserv);
3020 mod_chanmode_announce(chanserv, channel, &change);
3021 reply("CSMSG_OPCHAN_DONE", channel->name);
3022 return 1;
3023 }
3024
3025 static CHANSERV_FUNC(cmd_adduser)
3026 {
3027 struct userData *actee;
3028 struct userData *actor, *real_actor;;
3029 struct handle_info *handle;
3030 unsigned short access, override = 0;
3031
3032 REQUIRE_PARAMS(3);
3033
3034 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
3035 {
3036 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
3037 return 0;
3038 }
3039
3040 access = user_level_from_name(argv[2], UL_OWNER);
3041 if(!access)
3042 {
3043 reply("CSMSG_INVALID_ACCESS", argv[2]);
3044 return 0;
3045 }
3046
3047 actor = GetChannelUser(channel->channel_info, user->handle_info);
3048 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
3049
3050 if(actor->access <= access)
3051 {
3052 reply("CSMSG_NO_BUMP_ACCESS");
3053 return 0;
3054 }
3055
3056 /* Trying to add someone with equal/more access? */
3057 if (!real_actor || real_actor->access <= access)
3058 override = CMD_LOG_OVERRIDE;
3059
3060 if(!(handle = modcmd_get_handle_info(user, argv[1])))
3061 {
3062 // 'kevin must first authenticate with AuthServ.' is sent to user
3063 struct userNode *unode;
3064 unode = GetUserH(argv[1]); /* find user struct by nick */
3065 if(unode)
3066 {
3067 if(find_adduser_pending(channel, unode)) {
3068 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name);
3069 }
3070 else {
3071 if(IsInChannel(channel, unode)) {
3072 reply("CSMSG_ADDUSER_PENDING");
3073 add_adduser_pending(channel, unode, access);
3074 send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
3075 }
3076 /* this results in user must auth AND not in chan errors. too confusing..
3077 else {
3078 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3079 }
3080 */
3081 }
3082 }
3083 return 0;
3084 }
3085
3086 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
3087 {
3088 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
3089 return 0;
3090 }
3091
3092 time_t accessexpiry = 0;
3093 unsigned int duration = 0;
3094 if (argc > 3) {
3095 if ((duration = ParseInterval(argv[3])))
3096 accessexpiry = now + duration;
3097 }
3098
3099 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL, accessexpiry);
3100 scan_user_presence(actee, NULL);
3101
3102 if (duration > 0)
3103 timeq_add(accessexpiry, chanserv_expire_tempuser, actee);
3104
3105 reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
3106 return 1 | override;
3107 }
3108
3109 static CHANSERV_FUNC(cmd_clvl)
3110 {
3111 struct handle_info *handle;
3112 struct userData *victim;
3113 struct userData *actor, *real_actor;
3114 unsigned short new_access, override = 0;
3115 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3116
3117 REQUIRE_PARAMS(3);
3118
3119 actor = GetChannelUser(channel->channel_info, user->handle_info);
3120 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
3121
3122 if(!(handle = modcmd_get_handle_info(user, argv[1])))
3123 return 0;
3124
3125 if(handle == user->handle_info && !privileged)
3126 {
3127 reply("CSMSG_NO_SELF_CLVL");
3128 return 0;
3129 }
3130
3131 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
3132 {
3133 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
3134 return 0;
3135 }
3136
3137 if(actor->access <= victim->access && !privileged)
3138 {
3139 reply("MSG_USER_OUTRANKED", handle->handle);
3140 return 0;
3141 }
3142
3143 new_access = user_level_from_name(argv[2], UL_OWNER);
3144
3145 if(!new_access)
3146 {
3147 reply("CSMSG_INVALID_ACCESS", argv[2]);
3148 return 0;
3149 }
3150
3151 if(new_access >= actor->access && !privileged)
3152 {
3153 reply("CSMSG_NO_BUMP_ACCESS");
3154 return 0;
3155 }
3156
3157 time_t clvlexpiry = 0;
3158 unsigned int duration = 0;
3159 if (argc > 3) {
3160 if ((duration = ParseInterval(argv[3])))
3161 clvlexpiry = now + duration;
3162 }
3163
3164 if (duration > 0) {
3165 if (victim->accessexpiry > 0) {
3166 reply("CSMSG_NO_BUMP_EXPIRY");
3167 return 0;
3168 }
3169
3170 victim->clvlexpiry = clvlexpiry;
3171 victim->lastaccess = victim->access;
3172 timeq_add(clvlexpiry, chanserv_expire_tempclvl, victim);
3173 }
3174
3175 /* Trying to clvl a equal/higher user? */
3176 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
3177 override = CMD_LOG_OVERRIDE;
3178 /* Trying to clvl someone to equal/higher access? */
3179 if(!real_actor || new_access >= real_actor->access)
3180 override = CMD_LOG_OVERRIDE;
3181 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3182 * If they lower their own access it's not a big problem.
3183 */
3184
3185 victim->access = new_access;
3186 reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
3187
3188 return 1 | override;
3189 }
3190
3191 static CHANSERV_FUNC(cmd_deluser)
3192 {
3193 struct handle_info *handle;
3194 struct userData *victim;
3195 struct userData *actor, *real_actor;
3196 unsigned short access, override = 0;
3197 char *chan_name;
3198
3199 REQUIRE_PARAMS(2);
3200
3201 actor = GetChannelUser(channel->channel_info, user->handle_info);
3202 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
3203
3204 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
3205 return 0;
3206
3207 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
3208 {
3209 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
3210 return 0;
3211 }
3212
3213 if(argc > 2)
3214 {
3215 access = user_level_from_name(argv[1], UL_OWNER);
3216 char *useraccess = user_level_name_from_level(victim->access);
3217 if(!access)
3218 {
3219 reply("CSMSG_INVALID_ACCESS", argv[1]);
3220 return 0;
3221 }
3222 if(strcasecmp(argv[1], useraccess))
3223 {
3224 reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
3225 return 0;
3226 }
3227 }
3228 else
3229 {
3230 access = victim->access;
3231 }
3232
3233 if((actor->access <= victim->access) && !IsHelping(user))
3234 {
3235 reply("MSG_USER_OUTRANKED", victim->handle->handle);
3236 return 0;
3237 }
3238
3239 /* If people delete themselves it is an override, but they
3240 * could've used deleteme so we don't log it as an override
3241 */
3242 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
3243 override = CMD_LOG_OVERRIDE;
3244
3245 chan_name = strdup(channel->name);
3246 del_channel_user(victim, 1);
3247 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
3248 free(chan_name);
3249
3250 return 1 | override;
3251 }
3252
3253 static int
3254 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
3255 {
3256 struct userData *actor, *real_actor, *uData, *next;
3257 unsigned int override = 0;
3258
3259 actor = GetChannelUser(channel->channel_info, user->handle_info);
3260 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
3261
3262 if(min_access > max_access)
3263 {
3264 reply("CSMSG_BAD_RANGE", min_access, max_access);
3265 return 0;
3266 }
3267
3268 if((actor->access <= max_access) && !IsHelping(user))
3269 {
3270 reply("CSMSG_NO_ACCESS");
3271 return 0;
3272 }
3273
3274 if(!real_actor || real_actor->access <= max_access)
3275 override = CMD_LOG_OVERRIDE;
3276
3277 for(uData = channel->channel_info->users; uData; uData = next)
3278 {
3279 next = uData->next;
3280
3281 if((uData->access >= min_access)
3282 && (uData->access <= max_access)
3283 && match_ircglob(uData->handle->handle, mask))
3284 del_channel_user(uData, 1);
3285 }
3286
3287 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
3288
3289 return 1 | override;
3290 }
3291
3292 static CHANSERV_FUNC(cmd_mdelowner)
3293 {
3294 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
3295 }
3296
3297 static CHANSERV_FUNC(cmd_mdelcoowner)
3298 {
3299 return cmd_mdel_user(user, channel, UL_COOWNER, UL_OWNER-1, argv[1], cmd);
3300 }
3301
3302 static CHANSERV_FUNC(cmd_mdelmanager)
3303 {
3304 return cmd_mdel_user(user, channel, UL_MANAGER, UL_COOWNER-1, argv[1], cmd);
3305 }
3306
3307 static CHANSERV_FUNC(cmd_mdelop)
3308 {
3309 return cmd_mdel_user(user, channel, UL_OP, UL_MANAGER-1, argv[1], cmd);
3310 }
3311
3312 static CHANSERV_FUNC(cmd_mdelhalfop)
3313 {
3314 return cmd_mdel_user(user, channel, UL_HALFOP, UL_OP-1, argv[1], cmd);
3315 }
3316
3317 static CHANSERV_FUNC(cmd_mdelpeon)
3318 {
3319 return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
3320 }
3321
3322
3323 static CHANSERV_FUNC(cmd_levels)
3324 {
3325 struct helpfile_table tbl;
3326 int ii = 0;
3327
3328 tbl.length = 6 + 1; // 6 levels
3329 tbl.width = 4;
3330 tbl.flags = 0;
3331 tbl.contents = calloc(tbl.length,sizeof(tbl.contents[0]));
3332 tbl.contents[0] = calloc(tbl.width,sizeof(tbl.contents[0][0]));
3333 tbl.contents[0][0] = "Level";
3334 tbl.contents[0][1] = "From";
3335 tbl.contents[0][2] = "-";
3336 tbl.contents[0][3] = "To";
3337
3338 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3339 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OWNER));
3340 tbl.contents[ii][1] = msnprintf(4, "%d", UL_OWNER);
3341 tbl.contents[ii][2] = msnprintf(2, " ");
3342 tbl.contents[ii][3] = msnprintf(1, "");
3343
3344 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3345 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_COOWNER));
3346 tbl.contents[ii][1] = msnprintf(4, "%d", UL_COOWNER);
3347 tbl.contents[ii][2] = msnprintf(2, "-");
3348 tbl.contents[ii][3] = msnprintf(4, "%d", UL_OWNER-1);
3349
3350 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3351 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_MANAGER));
3352 tbl.contents[ii][1] = msnprintf(4, "%d", UL_MANAGER);
3353 tbl.contents[ii][2] = msnprintf(2, "-");
3354 tbl.contents[ii][3] = msnprintf(4, "%d", UL_COOWNER-1);
3355
3356 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3357 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OP));
3358 tbl.contents[ii][1] = msnprintf(4, "%d", UL_OP);
3359 tbl.contents[ii][2] = msnprintf(2, "-");
3360 tbl.contents[ii][3] = msnprintf(4, "%d", UL_MANAGER-1);
3361
3362 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3363 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_HALFOP));
3364 tbl.contents[ii][1] = msnprintf(4, "%d", UL_HALFOP);
3365 tbl.contents[ii][2] = msnprintf(2, "-");
3366 tbl.contents[ii][3] = msnprintf(4, "%d", UL_OP-1);
3367
3368 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3369 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_PEON));
3370 tbl.contents[ii][1] = msnprintf(4, "%d", UL_PEON);
3371 tbl.contents[ii][2] = msnprintf(2, "-");
3372 tbl.contents[ii][3] = msnprintf(4, "%d", UL_HALFOP-1);
3373
3374 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3375 return 0;
3376
3377 /*
3378 reply("CSMSG_LEVELS_HEADER");
3379 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3380 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3381 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3382 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3383 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3384 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3385 reply("CSMSG_BAR");
3386 */
3387 }
3388
3389 /* trim_lamers.. */
3390 static int
3391 cmd_trim_bans(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned long duration)
3392 {
3393 struct banData *bData, *next;
3394 char interval[INTERVALLEN];
3395 unsigned int count;
3396 time_t limit;
3397
3398 count = 0;
3399 limit = now - duration;
3400 for(bData = channel->channel_info->bans; bData; bData = next)
3401 {
3402 next = bData->next;
3403
3404 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3405 continue;
3406
3407 del_channel_ban(bData);
3408 count++;
3409 }
3410
3411 intervalString(interval, duration, user->handle_info);
3412 reply("CSMSG_TRIMMED_LAMERS", count, channel->name, interval);
3413 return 1;
3414 }
3415
3416 static int
3417 cmd_trim_users(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3418 {
3419 struct userData *actor, *uData, *next;
3420 char interval[INTERVALLEN];
3421 unsigned int count;
3422 time_t limit;
3423
3424 actor = GetChannelUser(channel->channel_info, user->handle_info);
3425 actor = GetChannelAccess(channel->channel_info, user->handle_info);
3426
3427 if(min_access > max_access)
3428 {
3429 reply("CSMSG_BAD_RANGE", min_access, max_access);
3430 return 0;
3431 }
3432
3433 if(!actor || actor->access <= max_access)
3434 {
3435 reply("CSMSG_NO_ACCESS");
3436 return 0;
3437 }
3438
3439 count = 0;
3440 limit = now - duration;
3441 for(uData = channel->channel_info->users; uData; uData = next)
3442 {
3443 next = uData->next;
3444
3445 if((uData->seen > limit)
3446 || uData->present
3447 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3448 continue;
3449
3450 if(((uData->access >= min_access) && (uData->access <= max_access))
3451 || (!max_access && (uData->access < actor->access)))
3452 {
3453 del_channel_user(uData, 1);
3454 count++;
3455 }
3456 }
3457
3458 if(!max_access)
3459 {
3460 min_access = 1;
3461 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3462 }
3463 reply("CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3464 return 1;
3465 }
3466
3467 static CHANSERV_FUNC(cmd_trim)
3468 {
3469 unsigned long duration;
3470 unsigned short min_level, max_level;
3471 int vacation;
3472
3473 REQUIRE_PARAMS(3);
3474
3475 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3476 duration = ParseInterval(argv[2]);
3477 if(duration < 60)
3478 {
3479 reply("CSMSG_CANNOT_TRIM");
3480 return 0;
3481 }
3482
3483 if(!irccasecmp(argv[1], "lamers"))
3484 {
3485 cmd_trim_bans(cmd, user, channel, duration); /* trim_lamers.. */
3486 return 1;
3487 }
3488 else if(!irccasecmp(argv[1], "users"))
3489 {
3490 cmd_trim_users(cmd, user, channel, 0, 0, duration, vacation);
3491 return 1;
3492 }
3493 else if(parse_level_range(&min_level, &max_level, argv[1]))
3494 {
3495 cmd_trim_users(cmd, user, channel, min_level, max_level, duration, vacation);
3496 return 1;
3497 }
3498 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3499 {
3500 cmd_trim_users(cmd, user, channel, min_level, min_level, duration, vacation);
3501 return 1;
3502 }
3503 else
3504 {
3505 reply("CSMSG_INVALID_TRIM", argv[1]);
3506 return 0;
3507 }
3508 }
3509
3510 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3511 to the user. cmd_all takes advantage of this. */
3512 static CHANSERV_FUNC(cmd_up)
3513 {
3514 struct mod_chanmode change;
3515 struct userData *uData;
3516 const char *errmsg;
3517
3518 mod_chanmode_init(&change);
3519 change.argc = 1;
3520 change.args[0].u.member = GetUserMode(channel, user);
3521 if(!change.args[0].u.member)
3522 {
3523 if(argc)
3524 reply("MSG_CHANNEL_ABSENT", channel->name);
3525 return 0;
3526 }
3527
3528 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3529 if(!uData)
3530 {
3531 if(argc)
3532 reply("CSMSG_GODMODE_UP", argv[0]);
3533 return 0;
3534 }
3535 else if(uData->access >= UL_OP)
3536 {
3537 change.args[0].mode = MODE_CHANOP;
3538 errmsg = "CSMSG_ALREADY_OPPED";
3539 }
3540 else if(uData->access >= UL_HALFOP )
3541 {
3542 change.args[0].mode = MODE_HALFOP;
3543 errmsg = "CSMSG_ALREADY_HALFOPPED";
3544 }
3545 else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chAutomode] != 'm' ))
3546 {
3547 change.args[0].mode = MODE_VOICE;
3548 errmsg = "CSMSG_ALREADY_VOICED";
3549 }
3550 else
3551 {
3552 if(argc)
3553 reply("CSMSG_NO_ACCESS");
3554 return 0;
3555 }
3556 change.args[0].mode &= ~change.args[0].u.member->modes;
3557 if(!change.args[0].mode)
3558 {
3559 if(argc)
3560 reply(errmsg, channel->name);
3561 return 0;
3562 }
3563 modcmd_chanmode_announce(&change);
3564 return 1;
3565 }
3566
3567 static CHANSERV_FUNC(cmd_down)
3568 {
3569 struct mod_chanmode change;
3570
3571 mod_chanmode_init(&change);
3572 change.argc = 1;
3573 change.args[0].u.member = GetUserMode(channel, user);
3574 if(!change.args[0].u.member)
3575 {
3576 if(argc)
3577 reply("MSG_CHANNEL_ABSENT", channel->name);
3578 return 0;
3579 }
3580
3581 if(!change.args[0].u.member->modes)
3582 {
3583 if(argc)
3584 reply("CSMSG_ALREADY_DOWN", channel->name);
3585 return 0;
3586 }
3587
3588 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3589 modcmd_chanmode_announce(&change);
3590 return 1;
3591 }
3592
3593 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3594 {
3595 struct userData *cList;
3596
3597 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3598 {
3599 if(IsSuspended(cList->channel)
3600 || IsUserSuspended(cList)
3601 || !GetUserMode(cList->channel->channel, user))
3602 continue;
3603
3604 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3605 }
3606
3607 return 1;
3608 }
3609
3610 static CHANSERV_FUNC(cmd_upall)
3611 {
3612 return cmd_all(CSFUNC_ARGS, cmd_up);
3613 }
3614
3615 static CHANSERV_FUNC(cmd_downall)
3616 {
3617 return cmd_all(CSFUNC_ARGS, cmd_down);
3618 }
3619
3620 typedef int validate_func_t(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim);
3621 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3622
3623 static int
3624 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3625 {
3626 unsigned int ii, valid;
3627 struct userNode *victim;
3628 struct mod_chanmode *change;
3629
3630 change = mod_chanmode_alloc(argc - 1);
3631
3632 for(ii=valid=0; ++ii < argc; )
3633 {
3634 if(!(victim = GetUserH(argv[ii])))
3635 continue;
3636 change->args[valid].mode = mode;
3637 change->args[valid].u.member = GetUserMode(channel, victim);
3638 if(!change->args[valid].u.member)
3639 continue;
3640 if(validate && !validate(cmd, user, channel, victim))
3641 continue;
3642 valid++;
3643 }
3644
3645 change->argc = valid;
3646 if(valid < (argc-1))
3647 reply("CSMSG_PROCESS_FAILED");
3648 if(valid)
3649 {
3650 modcmd_chanmode_announce(change);
3651 reply(action, channel->name);
3652 }
3653 mod_chanmode_free(change);
3654 return 1;
3655 }
3656
3657 static CHANSERV_FUNC(cmd_op)
3658 {
3659 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3660 }
3661
3662 static CHANSERV_FUNC(cmd_hop)
3663 {
3664 return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS");
3665 }
3666
3667 static CHANSERV_FUNC(cmd_deop)
3668 {
3669 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3670 }
3671
3672 static CHANSERV_FUNC(cmd_dehop)
3673 {
3674 return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS");
3675 }
3676
3677 static CHANSERV_FUNC(cmd_voice)
3678 {
3679 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3680 }
3681
3682 static CHANSERV_FUNC(cmd_devoice)
3683 {
3684 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3685 }
3686
3687 static int
3688 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3689 {
3690 unsigned int ii;
3691
3692 if(victimCount)
3693 *victimCount = 0;
3694 for(ii=0; ii<channel->members.used; ii++)
3695 {
3696 struct modeNode *mn = channel->members.list[ii];
3697
3698 if(IsService(mn->user))
3699 continue;
3700
3701 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3702 continue;
3703
3704 if(protect_user(mn->user, user, channel->channel_info, false))
3705 return 1;
3706
3707 if(victims)
3708 victims[(*victimCount)++] = mn;
3709 }
3710 return 0;
3711 }
3712
3713 static int
3714 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3715 {
3716 struct userNode *victim;
3717 struct modeNode **victims;
3718 unsigned int offset, n, victimCount, duration = 0;
3719 char *reason = "Bye.", *ban, *name;
3720 char interval[INTERVALLEN];
3721
3722 offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
3723 REQUIRE_PARAMS(offset);
3724 if(argc > offset)
3725 {
3726 reason = unsplit_string(argv + offset, argc - offset, NULL);
3727 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3728 {
3729 /* Truncate the reason to a length of TOPICLEN, as
3730 the ircd does; however, leave room for an ellipsis
3731 and the kicker's nick. */
3732 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3733 }
3734 }
3735
3736 if((victim = GetUserH(argv[1])))
3737 {
3738 victims = alloca(sizeof(victims[0]));
3739 victims[0] = GetUserMode(channel, victim);
3740 /* XXX: The comparison with ACTION_KICK is just because all
3741 * other actions can work on users outside the channel, and we
3742 * want to allow those (e.g. unbans) in that case. If we add
3743 * some other ejection action for in-channel users, change
3744 * this too. */
3745 victimCount = victims[0] ? 1 : 0;
3746
3747 if(IsService(victim))
3748 {
3749 if(cmd)
3750 reply("MSG_SERVICE_IMMUNE", victim->nick);
3751 return 0;
3752 }
3753
3754 if((action == ACTION_KICK) && !victimCount)
3755 {
3756 if(cmd)
3757 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3758 return 0;
3759 }
3760
3761 if(protect_user(victim, user, channel->channel_info, false))
3762 {
3763 // This translates to send_message(user, cmd->parent->bot, ...)
3764 // if user is x3 (ctcp action) cmd is null and segfault.
3765 if(cmd)
3766 reply("CSMSG_USER_PROTECTED", victim->nick);
3767 return 0;
3768 }
3769
3770 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3771 name = victim->nick;
3772 }
3773 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3774 {
3775 struct handle_info *hi;
3776 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3777 const char *accountname = argv[1] + 1;
3778
3779 if(!(hi = get_handle_info(accountname)))
3780 {
3781 reply("MSG_HANDLE_UNKNOWN", accountname);
3782 return 0;
3783 }
3784
3785 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3786 victims = alloca(sizeof(victims[0]) * channel->members.used);
3787
3788 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3789 {
3790 reply("CSMSG_MASK_PROTECTED", banmask);
3791 return 0;
3792 }
3793
3794 if((action == ACTION_KICK) && (victimCount == 0))
3795 {
3796 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3797 return 0;
3798 }
3799
3800 name = ban = strdup(banmask);
3801 }
3802 else
3803 {
3804 if(!is_ircmask(argv[1]))
3805 {
3806 if(cmd)
3807 reply("MSG_NICK_UNKNOWN", argv[1]);
3808 return 0;
3809 }
3810
3811 victims = alloca(sizeof(victims[0]) * channel->members.used);
3812
3813 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3814 {
3815 if(cmd)
3816 reply("CSMSG_MASK_PROTECTED", argv[1]);
3817 return 0;
3818 }
3819 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3820 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3821 And, ^^^^^^^^^ BAH!
3822 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3823 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3824 some creativity, but its not x3's job to be the ban censor anyway. */
3825 if(is_overmask(argv[1]))
3826 {
3827 if(cmd)
3828 reply("CSMSG_LAME_MASK", argv[1]);
3829 return 0;
3830 }
3831
3832 if((action == ACTION_KICK) && (victimCount == 0))
3833 {
3834 if(cmd)
3835 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3836 return 0;
3837 }
3838
3839 name = ban = strdup(argv[1]);
3840 }
3841
3842 /* Truncate the ban in place if necessary; we must ensure
3843 that 'ban' is a valid ban mask before sanitizing it. */
3844 sanitize_ircmask(ban);
3845
3846 if(action & ACTION_ADD_LAMER)
3847 {
3848 struct banData *bData, *next;
3849
3850 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
3851 {
3852 if(cmd)
3853 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
3854 free(ban);
3855 return 0;
3856 }
3857
3858 if(action & ACTION_ADD_TIMED_LAMER)
3859 {
3860 duration = ParseInterval(argv[2]);
3861
3862 if(duration < 15)
3863 {
3864 if(cmd)
3865 reply("CSMSG_DURATION_TOO_LOW");
3866 free(ban);
3867 return 0;
3868 }
3869 else if(duration > (86400 * 365 * 2))
3870 {
3871 if(cmd)
3872 reply("CSMSG_DURATION_TOO_HIGH");
3873 free(ban);
3874 return 0;
3875 }
3876 }
3877
3878 /* lamers... */
3879 for(bData = channel->channel_info->bans; bData; bData = next)
3880 {
3881 if(match_ircglobs(bData->mask, ban))
3882 {
3883 int exact = !irccasecmp(bData->mask, ban);
3884
3885 /* The ban is redundant; there is already a ban
3886 with the same effect in place. */
3887 if(exact)
3888 {
3889 if(bData->reason)
3890 free(bData->reason);
3891 bData->reason = strdup(reason);
3892 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3893 if(cmd)
3894 reply("CSMSG_REASON_CHANGE", ban);
3895 if(!bData->expires)
3896 goto post_add_ban;
3897 }
3898 if(exact && bData->expires)
3899 {
3900 int reset = 0;
3901
3902 /* If the ban matches an existing one exactly,
3903 extend the expiration time if the provided
3904 duration is longer. */
3905 if(duration && ((time_t)(now + duration) > bData->expires))
3906 {
3907 bData->expires = now + duration;
3908 reset = 1;
3909 }
3910 else if(!duration)
3911 {
3912 bData->expires = 0;
3913 reset = 1;
3914 }
3915
3916 if(reset)
3917 {
3918 /* Delete the expiration timeq entry and
3919 requeue if necessary. */
3920 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3921
3922 if(bData->expires)
3923 timeq_add(bData->expires, expire_ban, bData);
3924
3925 if(!cmd)
3926 {
3927 /* automated kickban, dont reply */
3928 }
3929 else if(duration)
3930 reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3931 else
3932 reply("CSMSG_LAMER_ADDED", name, channel->name);
3933
3934 goto post_add_ban;
3935 }
3936 }
3937 if(cmd)
3938 reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
3939
3940 free(ban);
3941 return 0;
3942 }
3943
3944 next = bData->next;
3945 if(match_ircglobs(ban, bData->mask))
3946 {
3947 /* The ban we are adding makes previously existing
3948 bans redundant; silently remove them. */
3949 del_channel_ban(bData);
3950 }
3951 }
3952
3953 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3954 free(ban);
3955 name = ban = strdup(bData->mask);
3956 }
3957 else if(ban)
3958 {
3959 /* WHAT DOES THIS DO?? -Rubin */
3960 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3961 {
3962 extern const char *hidden_host_suffix;
3963 const char *old_name = chanserv_conf.old_ban_names->list[n];
3964 char *new_mask;
3965 unsigned int l1, l2;
3966
3967 l1 = strlen(ban);
3968 l2 = strlen(old_name);
3969 if(l2+2 > l1)
3970 continue;
3971 if(irccasecmp(ban + l1 - l2, old_name))
3972 continue;
3973 new_mask = malloc(MAXLEN);
3974 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3975 free(ban);
3976 name = ban = new_mask;
3977 }
3978 }
3979
3980 post_add_ban:
3981 if(action & ACTION_BAN)
3982 {
3983 unsigned int exists;
3984 struct mod_chanmode *change;
3985
3986 if(channel->banlist.used >= MAXBANS)
3987 {
3988 if(cmd)
3989 reply("CSMSG_BANLIST_FULL", channel->name);
3990 free(ban);
3991 return 0;
3992 }
3993
3994 exists = ChannelBanExists(channel, ban);
3995 change = mod_chanmode_alloc(victimCount + 1);
3996 for(n = 0; n < victimCount; ++n)
3997 {
3998 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
3999 change->args[n].u.member = victims[n];
4000 }
4001 if(!exists)
4002 {
4003 change->args[n].mode = MODE_BAN;
4004 change->args[n++].u.hostmask = ban;
4005 }
4006 change->argc = n;
4007 if(cmd)
4008 modcmd_chanmode_announce(change);
4009 else
4010 mod_chanmode_announce(chanserv, channel, change);
4011 mod_chanmode_free(change);
4012
4013 if(exists && (action == ACTION_BAN))
4014 {
4015 if(cmd)
4016 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
4017 free(ban);
4018 return 0;
4019 }
4020 }
4021
4022 if(action & ACTION_ADD_LAMER)
4023 {
4024 char kick_reason[MAXLEN];
4025 sprintf(kick_reason, "(%s) %s", user->nick, reason);
4026
4027 for(n = 0; n < victimCount; n++) {
4028 if(!protect_user(victims[n]->user, user, channel->channel_info, true)) {
4029 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
4030 }
4031 }
4032 }
4033 else if(action & ACTION_KICK)
4034 {
4035 char kick_reason[MAXLEN];
4036 sprintf(kick_reason, "(%s) %s", user->nick, reason);
4037
4038 for(n = 0; n < victimCount; n++) {
4039 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
4040 }
4041 }
4042
4043 if(!cmd)
4044 {
4045 /* No response, since it was automated. */
4046 }
4047 else if(action & ACTION_ADD_LAMER)
4048 {
4049 if(duration)
4050 reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
4051 else
4052 reply("CSMSG_LAMER_ADDED", name, channel->name);
4053 }
4054 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
4055 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
4056 else if(action & ACTION_BAN)
4057 reply("CSMSG_BAN_DONE", name, channel->name);
4058 else if(action & ACTION_KICK && victimCount)
4059 reply("CSMSG_KICK_DONE", name, channel->name);
4060
4061 free(ban);
4062 return 1;
4063 }
4064
4065 static CHANSERV_FUNC(cmd_kickban)
4066 {
4067 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
4068 }
4069
4070 static CHANSERV_FUNC(cmd_kick)
4071 {
4072 return eject_user(CSFUNC_ARGS, ACTION_KICK);
4073 }
4074
4075 static CHANSERV_FUNC(cmd_ban)
4076 {
4077 return eject_user(CSFUNC_ARGS, ACTION_BAN);
4078 }
4079
4080 static CHANSERV_FUNC(cmd_addlamer)
4081 {
4082 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER);
4083 }
4084
4085 static CHANSERV_FUNC(cmd_addtimedlamer)
4086 {
4087 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER);
4088 }
4089
4090 static struct mod_chanmode *
4091 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
4092 {
4093 struct mod_chanmode *change;
4094 unsigned char *match;
4095 unsigned int ii, count;
4096
4097 match = alloca(bans->used);
4098 if(actee)
4099 {
4100 for(ii = count = 0; ii < bans->used; ++ii)
4101 {
4102 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
4103 MATCH_USENICK | MATCH_VISIBLE);
4104 if(match[ii])
4105 count++;
4106 }
4107 }
4108 else
4109 {
4110 for(ii = count = 0; ii < bans->used; ++ii)
4111 {
4112 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
4113 if(match[ii])
4114 count++;
4115 }
4116 }
4117 if(!count)
4118 return NULL;
4119 change = mod_chanmode_alloc(count);
4120 for(ii = count = 0; ii < bans->used; ++ii)
4121 {
4122 if(!match[ii])
4123 continue;
4124 change->args[count].mode = MODE_REMOVE | MODE_BAN;
4125 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
4126 }
4127 assert(count == change->argc);
4128 return change;
4129 }
4130
4131 void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
4132 {
4133 unsigned int jj, ii, count;
4134 struct banNode *bn;
4135 struct chanData *channel;
4136 time_t bantimeout;
4137 struct mod_chanmode *change;
4138
4139 log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
4140 /* Walk through every channel */
4141 for(channel = channelList; channel; channel = channel->next) {
4142 switch(channel->chOpts[chBanTimeout])
4143 {
4144 default: case '0': continue; /* Dont remove bans in this chan */
4145 case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */
4146 case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */
4147 case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */
4148 case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */
4149 case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
4150 }
4151 count = 0;
4152 /* First find out how many bans were going to unset */
4153 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
4154 if(channel->channel->banlist.list[jj]->set < bantimeout)
4155 count++;
4156 }
4157 if(count > 0) {
4158 /* At least one ban, so setup a removal */
4159 change = mod_chanmode_alloc(count);
4160 ii = 0;
4161 /* Walk over every ban in this channel.. */
4162 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
4163 bn = channel->channel->banlist.list[jj];
4164 if (bn->set < bantimeout) {
4165 log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
4166
4167 /* Add this ban to the mode change */
4168 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
4169 change->args[ii].u.hostmask = strdup(bn->ban);
4170 ii++;
4171 /* Pull this ban out of the list */
4172 banList_remove(&(channel->channel->banlist), bn);
4173 jj--;
4174 free(bn);
4175 }
4176 }
4177 /* Send the modes to IRC */
4178 mod_chanmode_announce(chanserv, channel->channel, change);
4179
4180 /* free memory from strdup above */
4181 for(ii = 0; ii < count; ++ii)
4182 free((char*)change->args[ii].u.hostmask);
4183
4184 mod_chanmode_free(change);
4185 }
4186 }
4187 /* Set this function to run again */
4188 if(chanserv_conf.ban_timeout_frequency)
4189 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
4190 }
4191
4192
4193 static int
4194 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
4195 {
4196 struct userNode *actee;
4197 char *mask = NULL;
4198 int acted = 0;
4199
4200 REQUIRE_PARAMS(2);
4201
4202 /* may want to allow a comma delimited list of users... */
4203 if(!(actee = GetUserH(argv[1])))
4204 {
4205 if(!is_ircmask(argv[1]) && *argv[1] == '*')
4206 {
4207 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
4208 const char *accountname = argv[1] + 1;
4209
4210 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
4211 mask = strdup(banmask);
4212 }
4213 else if(!is_ircmask(argv[1]))
4214 {
4215 reply("MSG_NICK_UNKNOWN", argv[1]);
4216 return 0;
4217 }
4218 else
4219 {
4220 mask = strdup(argv[1]);
4221 }
4222 }
4223
4224 /* We don't sanitize the mask here because ircu
4225 doesn't do it. */
4226 if(action & ACTION_UNBAN)
4227 {
4228 struct mod_chanmode *change;
4229 change = find_matching_bans(&channel->banlist, actee, mask);
4230 if(change)
4231 {
4232 unsigned int ii;
4233
4234 modcmd_chanmode_announce(change);
4235 for(ii = 0; ii < change->argc; ++ii)
4236 free((char*)change->args[ii].u.hostmask);
4237 mod_chanmode_free(change);
4238 acted = 1;
4239 }
4240 }
4241
4242 if(action & ACTION_DEL_LAMER)
4243 {
4244 struct banData *ban, *next;
4245
4246 ban = channel->channel_info->bans; /* lamers */
4247 while(ban)
4248 {
4249 if(actee)
4250 for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE);
4251 ban = ban->next);
4252 else
4253 for( ; ban && !match_ircglobs(mask, ban->mask);
4254 ban = ban->next);
4255 if(!ban)
4256 break;
4257 next = ban->next;
4258 del_channel_ban(ban);
4259 ban = next;
4260 acted = 1;
4261 }
4262 }
4263
4264 if(!acted)
4265 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
4266 else
4267 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
4268 if(mask)
4269 free(mask);
4270 return 1;
4271 }
4272
4273 static CHANSERV_FUNC(cmd_unban)
4274 {
4275 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
4276 }
4277
4278 static CHANSERV_FUNC(cmd_dellamer)
4279 {
4280 /* it doesn't necessarily have to remove the channel ban - may want
4281 to make that an option. */
4282 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_LAMER);
4283 }
4284
4285 static CHANSERV_FUNC(cmd_unbanme)
4286 {
4287 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
4288 long flags = ACTION_UNBAN;
4289
4290 /* remove permanent bans if the user has the proper access. */
4291 if(uData->access >= UL_MANAGER)
4292 flags |= ACTION_DEL_LAMER;
4293
4294 argv[1] = user->nick;
4295 return unban_user(user, channel, 2, argv, cmd, flags);
4296 }
4297
4298 static CHANSERV_FUNC(cmd_unbanall)
4299 {
4300 struct mod_chanmode *change;
4301 unsigned int ii;
4302
4303 if(!channel->banlist.used)
4304 {
4305 reply("CSMSG_NO_BANS", channel->name);
4306 return 0;
4307 }
4308
4309 change = mod_chanmode_alloc(channel->banlist.used);
4310 for(ii=0; ii<channel->banlist.used; ii++)
4311 {
4312 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
4313 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
4314 }
4315 modcmd_chanmode_announce(change);
4316 for(ii = 0; ii < change->argc; ++ii)
4317 free((char*)change->args[ii].u.hostmask);
4318 mod_chanmode_free(change);
4319 reply("CSMSG_BANS_REMOVED", channel->name);
4320 return 1;
4321 }
4322
4323 static CHANSERV_FUNC(cmd_open)
4324 {
4325 struct mod_chanmode *change;
4326 unsigned int ii;
4327
4328 change = find_matching_bans(&channel->banlist, user, NULL);
4329 if(!change)
4330 change = mod_chanmode_alloc(0);
4331 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
4332 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4333 && channel->channel_info->modes.modes_set)
4334 change->modes_clear &= ~channel->channel_info->modes.modes_set;
4335 modcmd_chanmode_announce(change);
4336 reply("CSMSG_CHANNEL_OPENED", channel->name);
4337 for(ii = 0; ii < change->argc; ++ii)
4338 free((char*)change->args[ii].u.hostmask);
4339 mod_chanmode_free(change);
4340 return 1;
4341 }
4342
4343 static CHANSERV_FUNC(cmd_myaccess)
4344 {
4345 static struct string_buffer sbuf;
4346 struct handle_info *target_handle;
4347 struct userData *uData;
4348
4349 if(argc < 2)
4350 target_handle = user->handle_info;
4351 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
4352 return 0;
4353 else if(!IsHelping(user) && target_handle != user->handle_info)
4354 {
4355 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
4356 return 0;
4357 }
4358 if(!target_handle->channels)
4359 {
4360 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
4361 return 1;
4362 }
4363
4364 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
4365 for(uData = target_handle->channels; uData; uData = uData->u_next)
4366 {
4367 struct chanData *cData = uData->channel;
4368
4369 if(uData->access > UL_OWNER)
4370 continue;
4371 if(IsProtected(cData)
4372 && (target_handle != user->handle_info)
4373 && !GetTrueChannelAccess(cData, user->handle_info))
4374 continue;
4375 sbuf.used = 0;
4376 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
4377 if(uData->flags == USER_AUTO_OP)
4378 string_buffer_append(&sbuf, ',');
4379 if(IsUserSuspended(uData))
4380 string_buffer_append(&sbuf, 's');
4381 if(IsUserAutoOp(uData))
4382 {
4383 if(uData->access >= UL_OP )
4384 string_buffer_append(&sbuf, 'o');
4385 else if(uData->access >= UL_HALFOP )
4386 string_buffer_append(&sbuf, 'h');
4387 else if(uData->access >= UL_PEON )
4388 string_buffer_append(&sbuf, 'v');
4389 }
4390 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
4391 string_buffer_append(&sbuf, 'i');
4392 if(IsUserAutoJoin(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
4393 string_buffer_append(&sbuf, 'j');
4394 if(uData->info)
4395 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
4396 else
4397 string_buffer_append_string(&sbuf, ")]");
4398 string_buffer_append(&sbuf, '\0');
4399 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
4400 }
4401
4402 return 1;
4403 }
4404
4405 static CHANSERV_FUNC(cmd_access)
4406 {
4407 struct userNode *target;
4408 struct handle_info *target_handle;
4409 struct userData *uData;
4410 int helping;
4411 char prefix[MAXLEN];
4412
4413 if(argc < 2)
4414 {
4415 target = user;
4416 target_handle = target->handle_info;
4417 }
4418 else if((target = GetUserH(argv[1])))
4419 {
4420 target_handle = target->handle_info;
4421 }
4422 else if(argv[1][0] == '*')
4423 {
4424 if(!(target_handle = get_handle_info(argv[1]+1)))
4425 {
4426 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
4427 return 0;
4428 }
4429 }
4430 else
4431 {
4432 reply("MSG_NICK_UNKNOWN", argv[1]);
4433 return 0;
4434 }
4435
4436 assert(target || target_handle);
4437
4438 if(target == chanserv)
4439 {
4440 reply("CSMSG_IS_CHANSERV");
4441 return 1;
4442 }
4443
4444 if(!target_handle)
4445 {
4446 if(IsOper(target))
4447 {
4448 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4449 return 0;
4450 }
4451 if(target != user)
4452 {
4453 reply("MSG_USER_AUTHENTICATE", target->nick);
4454 return 0;
4455 }
4456 reply("MSG_AUTHENTICATE");
4457 return 0;
4458 }
4459
4460 if(target)
4461 {
4462 const char *epithet = NULL, *type = NULL;
4463 if(IsOper(target))
4464 {
4465 epithet = chanserv_conf.irc_operator_epithet;
4466 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4467 }
4468 else if(IsNetworkHelper(target))
4469 {
4470 epithet = chanserv_conf.network_helper_epithet;
4471 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4472 }
4473 else if(IsSupportHelper(target))
4474 {
4475 epithet = chanserv_conf.support_helper_epithet;
4476 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4477 }
4478 if(epithet)
4479 {
4480 if(target_handle->epithet)
4481 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4482 else if(epithet)
4483 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4484 }
4485 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4486 }
4487 else
4488 {
4489 sprintf(prefix, "%s", target_handle->handle);
4490 }
4491
4492 if(!channel->channel_info)
4493 {
4494 reply("CSMSG_NOT_REGISTERED", channel->name);
4495 return 1;
4496 }
4497
4498 helping = HANDLE_FLAGGED(target_handle, HELPING)
4499 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4500 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4501 {
4502 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, user_level_name_from_level(uData->access), uData->access, channel->name);
4503 /* To prevent possible information leaks, only show infolines
4504 * if the requestor is in the channel or it's their own
4505 * handle. */
4506 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4507 {
4508 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4509 }
4510 /* Likewise, only say it's suspended if the user has active
4511 * access in that channel or it's their own entry. */
4512 if(IsUserSuspended(uData)
4513 && (GetChannelUser(channel->channel_info, user->handle_info)
4514 || (user->handle_info == uData->handle)))
4515 {
4516 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4517 }
4518 }
4519 else
4520 {
4521 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4522 }
4523
4524 return 1;
4525 }
4526
4527 /* This is never used...
4528 static void
4529 zoot_list(struct listData *list)
4530 {
4531 struct userData *uData;
4532 unsigned int start, curr, highest, lowest;
4533 struct helpfile_table tmp_table;
4534 const char **temp, *msg;
4535
4536 if(list->table.length == 1)
4537 {
4538 if(list->search)
4539 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4540 else
4541 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4542 msg = user_find_message(list->user, "MSG_NONE");
4543 send_message_type(4, list->user, list->bot, " %s", msg);
4544 }
4545 tmp_table.width = list->table.width;
4546 tmp_table.flags = list->table.flags;
4547 list->table.contents[0][0] = " ";
4548 highest = list->highest;
4549 if(list->lowest != 0)
4550 lowest = list->lowest;
4551 else if(highest < 100)
4552 lowest = 1;
4553 else
4554 lowest = highest - 100;
4555 for(start = curr = 1; curr < list->table.length; )
4556 {
4557 uData = list->users[curr-1];
4558 list->table.contents[curr++][0] = " ";
4559 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4560 {
4561 if(list->search)
4562 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest), list->search);
4563 else
4564 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest));
4565 temp = list->table.contents[--start];
4566 list->table.contents[start] = list->table.contents[0];
4567 tmp_table.contents = list->table.contents + start;
4568 tmp_table.length = curr - start;
4569 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4570 list->table.contents[start] = temp;
4571 start = curr;
4572 highest = lowest - 1;
4573 lowest = (highest < 100) ? 0 : (highest - 99);
4574 }
4575 }
4576 }
4577 */
4578
4579 static void
4580 normal_list(struct listData *list)
4581 {
4582 const char *msg;
4583 if(list->search)
4584 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4585 else
4586 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4587 if(list->table.length == 1)
4588 {
4589 msg = user_find_message(list->user, "MSG_NONE");
4590 send_message_type(4, list->user, list->bot, " %s", msg);
4591 }
4592 else
4593 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4594 }
4595
4596 /* if these need changed, uncomment and customize
4597 static void
4598 clean_list(struct listData *list)
4599 {
4600 const char *msg;
4601 if(list->search)
4602 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4603 else
4604 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4605 if(list->table.length == 1)
4606 {
4607 msg = user_find_message(list->user, "MSG_NONE");
4608 send_message_type(4, list->user, list->bot, " %s", msg);
4609 }
4610 else
4611 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4612 }
4613
4614 static void
4615 advanced_list(struct listData *list)
4616 {
4617 const char *msg;
4618 if(list->search)
4619 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4620 else
4621 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4622 if(list->table.length == 1)
4623 {
4624 msg = user_find_message(list->user, "MSG_NONE");
4625 send_message_type(4, list->user, list->bot, " %s", msg);
4626 }
4627 else
4628 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4629 }
4630
4631 static void
4632 classic_list(struct listData *list)
4633 {
4634 const char *msg;
4635 if(list->search)
4636 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4637 else
4638 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4639 if(list->table.length == 1)
4640 {
4641 msg = user_find_message(list->user, "MSG_NONE");
4642 send_message_type(4, list->user, list->bot, " %s", msg);
4643 }
4644 else
4645 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4646 }
4647 */
4648
4649 static int
4650 userData_access_comp(const void *arg_a, const void *arg_b)
4651 {
4652 const struct userData *a = *(struct userData**)arg_a;
4653 const struct userData *b = *(struct userData**)arg_b;
4654 int res;
4655 if(a->access != b->access)
4656 res = b->access - a->access;
4657 else
4658 res = irccasecmp(a->handle->handle, b->handle->handle);
4659 return res;
4660 }
4661
4662 static int
4663 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4664 {
4665 void (*send_list)(struct listData *);
4666 struct userData *uData;
4667 struct listData lData;
4668 unsigned int matches;
4669 const char **ary;
4670 int i = 0;
4671 int seen_index;
4672
4673 lData.user = user;
4674 lData.bot = cmd->parent->bot;
4675 lData.channel = channel;
4676 lData.lowest = lowest;
4677 lData.highest = highest;
4678 lData.search = (argc > 1) ? argv[1] : NULL;
4679 send_list = normal_list;
4680 /* What does the following line do exactly?? */
4681 /*(void)zoot_list; ** since it doesn't show user levels */
4682
4683 /*
4684 if(user->handle_info)
4685 {
4686 switch(user->handle_info->userlist_style)
4687 {
4688 case HI_STYLE_CLEAN:
4689 send_list = clean_list;
4690 break;
4691 case HI_STYLE_ADVANCED:
4692 send_list = advanced_list;
4693 break;
4694 case HI_STYLE_CLASSIC:
4695 send_list = classic_list;
4696 break;
4697 case HI_STYLE_NORMAL:
4698 default:
4699 send_list = normal_list;
4700 break;
4701 }
4702 }
4703 */
4704 send_list = normal_list;
4705
4706 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4707 matches = 0;
4708 for(uData = channel->channel_info->users; uData; uData = uData->next)
4709 {
4710 if((uData->access < lowest)
4711 || (uData->access > highest)
4712 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4713 continue;
4714 lData.users[matches++] = uData;
4715 }
4716 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4717
4718 lData.table.length = matches+1;
4719 lData.table.flags = TABLE_NO_FREE;
4720 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4721
4722 if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
4723 lData.table.width = 6; /* with level = 6 */
4724 else
4725 lData.table.width = 5; /* without = 5 */
4726 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4727 lData.table.contents[0] = ary;
4728 if(user->handle_info) {
4729 switch(user->handle_info->userlist_style) {
4730 case HI_STYLE_CLASSIC:
4731 ary[i++] = "Level";
4732 break;
4733 case HI_STYLE_ADVANCED:
4734 ary[i++] = "Access";
4735 ary[i++] = "Level";
4736 break;
4737 case HI_STYLE_CLEAN:
4738 ary[i++] = "Access";
4739 break;
4740 case HI_STYLE_NORMAL:
4741 default:
4742 ary[i++] = "Access";
4743 break;
4744 }
4745 }
4746 else {
4747 ary[i++] = "Access";
4748 }
4749 ary[i++] = "Account";
4750 ary[i] = "Last Seen";
4751 seen_index = i++;
4752 ary[i++] = "Status";
4753 ary[i++] = "Expiry";
4754 for(matches = 1; matches < lData.table.length; ++matches)
4755 {
4756 struct userData *uData = lData.users[matches-1];
4757 char seen[INTERVALLEN];
4758
4759 i = 0;
4760 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4761 lData.table.contents[matches] = ary;
4762 if(user->handle_info) {
4763 switch(user->handle_info->userlist_style) {
4764 case HI_STYLE_CLASSIC:
4765 ary[i++] = strtab(uData->access);
4766 break;
4767 case HI_STYLE_ADVANCED:
4768 ary[i++] = user_level_name_from_level(uData->access);
4769 ary[i++] = strtab(uData->access);
4770 break;
4771 case HI_STYLE_CLEAN:
4772 ary[i++] = user_level_name_from_level(uData->access);
4773 break;
4774 case HI_STYLE_NORMAL:
4775 default:
4776 ary[i++] = user_level_name_from_level(uData->access);
4777 break;
4778 }
4779 }
4780 else {
4781 ary[i++] = user_level_name_from_level(uData->access);
4782 }
4783 ary[i++] = uData->handle->handle;
4784 if(uData->present)
4785 ary[i] = "Here";
4786 else if(!uData->seen)
4787 ary[i] = "Never";
4788 else
4789 ary[i] = intervalString(seen, now - uData->seen, user->handle_info);
4790 ary[i] = strdup(ary[i]);
4791 i++;
4792 if(IsUserSuspended(uData))
4793 ary[i++] = "Suspended";
4794 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4795 ary[i++] = "Vacation";
4796 else
4797 ary[i++] = "Normal";
4798
4799 if ((uData->accessexpiry > 0) || (uData->clvlexpiry > 0)) {
4800 char delay[INTERVALLEN];
4801 time_t diff;
4802
4803 if (uData->accessexpiry > 0) {
4804 diff = uData->accessexpiry - now;
4805 intervalString(delay, diff, user->handle_info);
4806 } else {
4807 diff = uData->clvlexpiry - now;
4808 intervalString(delay, diff, user->handle_info);
4809 }
4810 ary[i++] = delay;
4811 } else
4812 ary[i++] = "Never";
4813 }
4814
4815 send_list(&lData);
4816 for(matches = 1; matches < lData.table.length; ++matches)
4817 {
4818 /* Free strdup above */
4819 free((char*)lData.table.contents[matches][seen_index]);
4820 free(lData.table.contents[matches]);
4821 }
4822 free(lData.table.contents[0]);
4823 free(lData.table.contents);
4824 return 1;
4825 }
4826
4827 /* Remove this now that debugging is over? or improve it for
4828 * users? Would it be better tied into USERS somehow? -Rubin */
4829 static CHANSERV_FUNC(cmd_pending)
4830 {
4831 struct adduserPending *ap;
4832 reply("CSMSG_ADDUSER_PENDING_HEADER");
4833 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4834 reply("CSMSG_BAR");
4835 for(ap = adduser_pendings;ap;ap = ap->next)
4836 reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
4837 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4838 return 1;
4839 }
4840
4841 static CHANSERV_FUNC(cmd_users)
4842 {
4843 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4844 }
4845
4846 static CHANSERV_FUNC(cmd_wlist)
4847 {
4848 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4849 }
4850
4851 static CHANSERV_FUNC(cmd_clist)
4852 {
4853 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4854 }
4855
4856 static CHANSERV_FUNC(cmd_mlist)
4857 {
4858 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
4859 }
4860
4861 static CHANSERV_FUNC(cmd_olist)
4862 {
4863 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
4864 }
4865
4866 static CHANSERV_FUNC(cmd_hlist)
4867 {
4868 return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1);
4869 }
4870
4871 static CHANSERV_FUNC(cmd_plist)
4872 {
4873 return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1);
4874 }
4875
4876 static CHANSERV_FUNC(cmd_lamers)
4877 {
4878 struct userNode *search_u = NULL;
4879 struct helpfile_table tbl;
4880 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4881 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4882 const char *msg_never, *triggered, *expires;
4883 struct banData *ban, **bans; /* lamers */
4884
4885 if(argc < 2)
4886 search = NULL;
4887 else if(strchr(search = argv[1], '!'))
4888 {
4889 search = argv[1];
4890 search_wilds = search[strcspn(search, "?*")];
4891 }
4892 else if(!(search_u = GetUserH(search)))
4893 reply("MSG_NICK_UNKNOWN", search);
4894
4895 reply("CSMSG_LAMERS_HEADER", channel->name);
4896 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
4897
4898 /* lamers */
4899 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4900 {
4901 if(search_u)
4902 {
4903 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4904 continue;
4905 }
4906 else if(search)
4907 {
4908 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4909 continue;
4910 }
4911 bans[matches++] = ban;
4912 if(ban->expires)
4913 timed = 1;
4914 }
4915
4916 tbl.length = matches + 1;
4917 tbl.width = 4 + timed;
4918 tbl.flags = 0;
4919 tbl.flags = TABLE_NO_FREE;
4920 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4921 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4922 tbl.contents[0][0] = "Mask";
4923 tbl.contents[0][1] = "Set By";
4924 tbl.contents[0][2] = "Triggered";
4925 if(timed)
4926 {
4927 tbl.contents[0][3] = "Expires";
4928 tbl.contents[0][4] = "Reason";
4929 }
4930 else
4931 tbl.contents[0][3] = "Reason";
4932 if(!matches)
4933 {
4934 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4935 /* reply("MSG_NONE"); */
4936 free(tbl.contents[0]);
4937 free(tbl.contents);
4938 return 0;
4939 }
4940
4941 msg_never = user_find_message(user, "MSG_NEVER");
4942 for(ii = 0; ii < matches; )
4943 {
4944 ban = bans[ii];
4945
4946 if(!timed)
4947 expires = "";
4948 else if(ban->expires)
4949 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4950 else
4951 expires = msg_never;
4952
4953 if(ban->triggered)
4954 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4955 else
4956 triggered = msg_never;
4957
4958 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4959 tbl.contents[ii][0] = ban->mask;
4960 tbl.contents[ii][1] = ban->owner;
4961 tbl.contents[ii][2] = strdup(triggered);
4962 if(timed)
4963 {
4964 tbl.contents[ii][3] = strdup(expires);
4965 tbl.contents[ii][4] = ban->reason;
4966 }
4967 else
4968 tbl.contents[ii][3] = ban->reason;
4969 }
4970 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4971 /* reply("MSG_MATCH_COUNT", matches); */
4972 for(ii = 1; ii < tbl.length; ++ii)
4973 {
4974 free((char*)tbl.contents[ii][2]);
4975 if(timed)
4976 free((char*)tbl.contents[ii][3]);
4977 free(tbl.contents[ii]);
4978 }
4979 free(tbl.contents[0]);
4980 free(tbl.contents);
4981 return 1;
4982 }
4983
4984 /* bad_topic
4985 *
4986 * return + if the user does NOT have the right to set the topic, and
4987 * the topic is changed.
4988 */
4989 static int
4990 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4991 {
4992 struct chanData *cData = channel->channel_info;
4993 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4994 return 0;
4995 else if(cData->topic)
4996 return irccasecmp(new_topic, cData->topic);
4997 else
4998 return 0;
4999 }
5000
5001 /* conform_topic
5002 *
5003 * Makes a givin topic fit into a givin topic mask and returns
5004 * the results.
5005 *
5006 * topic_mask - the mask to conform to
5007 * topic - the topic to make conform
5008 * new_topic - the pre-allocated char* to put the new topic into
5009 *
5010 * modifies: new_topic
5011 */
5012 void
5013 conform_topic(char* topic_mask, char* topic, char *new_topic)
5014 {
5015 //char *topic_mask = cData->topic_mask;
5016 char tchar;
5017 int pos=0, starpos=-1, dpos=0, len;
5018
5019 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
5020 {
5021 switch(tchar)
5022 {
5023 case '*':
5024 if(starpos != -1)
5025 {
5026 strcpy(new_topic, "");
5027 return;
5028 }
5029 len = strlen(topic);
5030 if((dpos + len) > TOPICLEN)
5031 len = TOPICLEN + 1 - dpos;
5032 memcpy(new_topic+dpos, topic, len);
5033 dpos += len;
5034 starpos = pos;
5035 break;
5036 case '\\': tchar = topic_mask[pos++]; /* and fall through */
5037 default: new_topic[dpos++] = tchar; break;
5038 }
5039 }
5040 if((dpos > TOPICLEN) || tchar)
5041 {
5042 strcpy(new_topic, "");
5043 return;
5044 }
5045 new_topic[dpos] = 0;
5046 return;
5047 }
5048
5049 static CHANSERV_FUNC(cmd_topic)
5050 {
5051 struct chanData *cData;
5052 char *topic;
5053 int p10 = 0;
5054
5055 #ifdef WITH_PROTOCOL_P10
5056 p10 = 1;
5057 #endif
5058
5059 cData = channel->channel_info;
5060 if(argc < 2)
5061 {
5062 if(cData->topic)
5063 {
5064 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5065 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, cData->topic, 1);
5066 reply("CSMSG_TOPIC_SET", cData->topic);
5067 return 1;
5068 }
5069
5070 reply("CSMSG_NO_TOPIC", channel->name);
5071 return 0;
5072 }
5073
5074 topic = unsplit_string(argv + 1, argc - 1, NULL);
5075 /* If they say "!topic *", use an empty topic. */
5076 if((topic[0] == '*') && (topic[1] == 0))
5077 topic[0] = 0;
5078
5079 if(bad_topic(channel, user, topic))
5080 {
5081 reply("CSMSG_TOPIC_LOCKED", channel->name);
5082 return 0;
5083 }
5084 else
5085 {
5086 /* If there is a topicmask set, and the new topic doesnt match, make it */
5087 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
5088 {
5089 char *topic_mask = cData->topic_mask;
5090 char new_topic[TOPICLEN+1];
5091
5092 /* make a new topic fitting mask */
5093 conform_topic(topic_mask, topic, new_topic);
5094 if(!*new_topic)
5095 {
5096 /* Topic couldnt fit into mask, was too long */
5097 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
5098 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
5099 return 0;
5100 }
5101 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, new_topic, 1);
5102 }
5103 else /* No mask set, just set the topic */
5104 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, topic, 1);
5105 }
5106
5107 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
5108 {
5109 /* Grab the topic and save it as the default topic. */
5110 free(cData->topic);
5111 cData->topic = strdup(channel->topic);
5112 }
5113
5114 return 1;
5115 }
5116
5117 static CHANSERV_FUNC(cmd_mode)
5118 {
5119 struct userData *uData;
5120 struct mod_chanmode *change;
5121 short base_oplevel;
5122
5123 if(argc < 2)
5124 {
5125 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
5126 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5127 return 0;
5128 }
5129
5130 change = &channel->channel_info->modes;
5131 if(change->modes_set || change->modes_clear) {
5132 modcmd_chanmode_announce(change);
5133 reply("CSMSG_DEFAULTED_MODES", channel->name);
5134 } else
5135 reply("CSMSG_NO_MODES", channel->name);
5136 return 1;
5137 }
5138
5139 uData = GetChannelUser(channel->channel_info, user->handle_info);
5140 if (!uData)
5141 base_oplevel = MAXOPLEVEL;
5142 else if (uData->access >= UL_OWNER)
5143 base_oplevel = 1;
5144 else
5145 base_oplevel = 1 + UL_OWNER - uData->access;
5146 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
5147
5148 if(!change)
5149 {
5150 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
5151 return 0;
5152 }
5153
5154 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5155 && mode_lock_violated(&channel->channel_info->modes, change))
5156 {
5157 char modes[MAXLEN];
5158 mod_chanmode_format(&channel->channel_info->modes, modes);
5159 reply("CSMSG_MODE_LOCKED", modes, channel->name);
5160 return 0;
5161 }
5162
5163 modcmd_chanmode_announce(change);
5164 mod_chanmode_free(change);
5165 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
5166 return 1;
5167 }
5168
5169 static CHANSERV_FUNC(cmd_invite)
5170 {
5171 struct userData *uData;
5172 struct userNode *invite;
5173
5174 uData = GetChannelUser(channel->channel_info, user->handle_info);
5175
5176 if(argc > 1)
5177 {
5178 if(!(invite = GetUserH(argv[1])))
5179 {
5180 reply("MSG_NICK_UNKNOWN", argv[1]);
5181 return 0;
5182 }
5183 }
5184 else
5185 invite = user;
5186
5187 if(GetUserMode(channel, invite))
5188 {
5189 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
5190 return 0;
5191 }
5192
5193 if(user != invite)
5194 {
5195 if(argc > 2)
5196 {
5197 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
5198 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
5199 }
5200 else
5201 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
5202 }
5203
5204 if (invite->handle_info && invite->handle_info->ignores->used && (argc > 1)) {
5205 unsigned int i;
5206 for (i=0; i < invite->handle_info->ignores->used; i++) {
5207 if (user_matches_glob(user, invite->handle_info->ignores->list[i], MATCH_USENICK)) {
5208 reply("CSMSG_CANNOT_INVITE", argv[1], channel->name);
5209 return 0;
5210 }
5211 }
5212 }
5213
5214 irc_invite(chanserv, invite, channel);
5215 if(argc > 1)
5216 reply("CSMSG_INVITED_USER", argv[1], channel->name);
5217
5218 return 1;
5219 }
5220
5221 static CHANSERV_FUNC(cmd_inviteme)
5222 {
5223 if(GetUserMode(channel, user))
5224 {
5225 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
5226 return 0;
5227 }
5228 if(channel->channel_info
5229 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5230 {
5231 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5232 return 0;
5233 }
5234 irc_invite(cmd->parent->bot, user, channel);
5235 return 1;
5236 }
5237
5238 static void
5239 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
5240 {
5241 unsigned int combo;
5242 char buf1[INTERVALLEN], buf2[INTERVALLEN];
5243
5244 /* We display things based on two dimensions:
5245 * - Issue time: present or absent
5246 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5247 * (in order of precedence, so something both expired and revoked
5248 * only counts as revoked)
5249 */
5250 combo = (suspended->issued ? 4 : 0)
5251 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
5252 switch(combo) {
5253 case 0: /* no issue time, indefinite expiration */
5254 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
5255 break;
5256 case 1: /* no issue time, expires in future */
5257 intervalString(buf1, suspended->expires-now, user->handle_info);
5258 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
5259 break;
5260 case 2: /* no issue time, expired */
5261 intervalString(buf1, now-suspended->expires, user->handle_info);
5262 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
5263 break;
5264 case 3: /* no issue time, revoked */
5265 intervalString(buf1, now-suspended->revoked, user->handle_info);
5266 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
5267 break;
5268 case 4: /* issue time set, indefinite expiration */
5269 intervalString(buf1, now-suspended->issued, user->handle_info);
5270 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
5271 break;
5272 case 5: /* issue time set, expires in future */
5273 intervalString(buf1, now-suspended->issued, user->handle_info);
5274 intervalString(buf2, suspended->expires-now, user->handle_info);
5275 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
5276 break;
5277 case 6: /* issue time set, expired */
5278 intervalString(buf1, now-suspended->issued, user->handle_info);
5279 intervalString(buf2, now-suspended->expires, user->handle_info);
5280 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
5281 break;
5282 case 7: /* issue time set, revoked */
5283 intervalString(buf1, now-suspended->issued, user->handle_info);
5284 intervalString(buf2, now-suspended->revoked, user->handle_info);
5285 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
5286 break;
5287 default:
5288 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
5289 return;
5290 }
5291 }
5292
5293 static void
5294 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
5295 {
5296 char buf[MAXLEN];
5297 const char *fmt = "%a %b %d %H:%M %Y";
5298 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
5299
5300 if(giveownership->staff_issuer)
5301 {
5302 if(giveownership->reason)
5303 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
5304 giveownership->target, giveownership->target_access,
5305 giveownership->staff_issuer, buf, giveownership->reason);
5306 else
5307 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
5308 giveownership->target, giveownership->target_access,
5309 giveownership->staff_issuer, buf);
5310 }
5311 else
5312 {
5313 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
5314 }
5315 }
5316
5317
5318 static CHANSERV_FUNC(cmd_info)
5319 {
5320 char modes[MAXLEN], buffer[INTERVALLEN];
5321 struct userData *uData, *owner;
5322 struct chanData *cData;
5323 struct do_not_register *dnr;
5324 struct note *note;
5325 dict_iterator_t it;
5326 int privileged;
5327
5328 cData = channel->channel_info;
5329 reply("CSMSG_CHANNEL_INFO", channel->name);
5330 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5331 reply("CSMSG_BAR");
5332
5333 uData = GetChannelUser(cData, user->handle_info);
5334 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
5335 {
5336 mod_chanmode_format(&cData->modes, modes);
5337 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
5338 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
5339 }
5340
5341 for(it = dict_first(cData->notes); it; it = iter_next(it))
5342 {
5343 int padding;
5344
5345 note = iter_data(it);
5346 if(!note_type_visible_to_user(cData, note->type, user))
5347 continue;
5348
5349 padding = PADLEN - 1 - strlen(iter_key(it));
5350 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
5351 }
5352
5353 reply("CSMSG_CHANNEL_MAX", cData->max);
5354 for(owner = cData->users; owner; owner = owner->next)
5355 if(owner->access == UL_OWNER)
5356 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
5357 reply("CSMSG_CHANNEL_USERS", cData->userCount);
5358 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
5359 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
5360
5361 privileged = IsStaff(user);
5362 /* if(privileged) */
5363 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
5364 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData->registrar)
5365 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
5366
5367 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
5368 chanserv_show_dnrs(user, cmd, channel->name, NULL);
5369
5370 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
5371 {
5372 struct suspended *suspended;
5373 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
5374 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
5375 show_suspension_info(cmd, user, suspended);
5376 }
5377 else if(IsSuspended(cData))
5378 {
5379 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
5380 show_suspension_info(cmd, user, cData->suspended);
5381 }
5382 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
5383 {
5384 struct giveownership *giveownership;
5385 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
5386 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
5387 show_giveownership_info(cmd, user, giveownership);
5388 }
5389 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5390 reply("CSMSG_CHANNEL_END");
5391 else
5392 reply("CSMSG_CHANNEL_END_CLEAN");
5393 return 1;
5394 }
5395
5396 static CHANSERV_FUNC(cmd_netinfo)
5397 {
5398 extern time_t boot_time;
5399 extern unsigned long burst_length;
5400 char interval[INTERVALLEN];
5401
5402 reply("CSMSG_NETWORK_INFO");
5403 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
5404 reply("CSMSG_NETWORK_USERS", dict_size(clients));
5405 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
5406 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
5407 reply("CSMSG_NETWORK_LAMERS", banCount);
5408 reply("CSMSG_NETWORK_CHANUSERS", userCount);
5409 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
5410 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
5411 return 1;
5412 }
5413
5414 static void
5415 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
5416 {
5417 struct helpfile_table table;
5418 unsigned int nn;
5419 struct userNode *user;
5420 char *nick;
5421
5422 table.length = 0;
5423 table.width = 1;
5424 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
5425 table.contents = alloca(list->used*sizeof(*table.contents));
5426 for(nn=0; nn<list->used; nn++)
5427 {
5428 user = list->list[nn];
5429 if(user->modes & skip_flags)
5430 continue;
5431 if(IsBot(user))
5432 continue;
5433 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
5434 if(IsAway(user))
5435 {
5436 nick = alloca(strlen(user->nick)+3);
5437 sprintf(nick, "(%s)", user->nick);
5438 }
5439 else
5440 nick = user->nick;
5441 table.contents[table.length][0] = nick;
5442 table.length++;
5443 }
5444 table_send(chanserv, to->nick, 0, NULL, table);
5445 }
5446
5447 static CHANSERV_FUNC(cmd_ircops)
5448 {
5449 reply("CSMSG_STAFF_OPERS");
5450 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
5451 return 1;
5452 }
5453
5454 static CHANSERV_FUNC(cmd_helpers)
5455 {
5456 reply("CSMSG_STAFF_HELPERS");
5457 send_staff_list(user, &curr_helpers, FLAGS_OPER);
5458 return 1;
5459 }
5460
5461 static CHANSERV_FUNC(cmd_staff)
5462 {
5463 reply("CSMSG_NETWORK_STAFF");
5464 cmd_ircops(CSFUNC_ARGS);
5465 cmd_helpers(CSFUNC_ARGS);
5466 return 1;
5467 }
5468
5469 static CHANSERV_FUNC(cmd_peek)
5470 {
5471 struct modeNode *mn;
5472 char modes[MODELEN];
5473 unsigned int n;
5474 struct helpfile_table table;
5475
5476 irc_make_chanmode(channel, modes);
5477
5478 reply("CSMSG_PEEK_INFO", channel->name);
5479 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5480 reply("CSMSG_BAR");
5481 reply("CSMSG_PEEK_TOPIC", channel->topic);
5482 reply("CSMSG_PEEK_MODES", modes);
5483 reply("CSMSG_PEEK_USERS", channel->members.used);
5484
5485 table.length = 0;
5486 table.width = 1;
5487 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
5488 table.contents = alloca(channel->members.used*sizeof(*table.contents));
5489 for(n = 0; n < channel->members.used; n++)
5490 {
5491 mn = channel->members.list[n];
5492 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
5493 continue;
5494 table.contents[table.length] = alloca(sizeof(**table.contents));
5495 table.contents[table.length][0] = mn->user->nick;
5496 table.length++;
5497 }
5498 if(table.length)
5499 {
5500 reply("CSMSG_PEEK_OPS");
5501 table_send(chanserv, user->nick, 0, NULL, table);
5502 }
5503 else
5504 reply("CSMSG_PEEK_NO_OPS");
5505 reply("CSMSG_PEEK_END");
5506 return 1;
5507 }
5508
5509 static MODCMD_FUNC(cmd_wipeinfo)
5510 {
5511 struct handle_info *victim;
5512 struct userData *ud, *actor, *real_actor;
5513 unsigned int override = 0;
5514
5515 REQUIRE_PARAMS(2);
5516 actor = GetChannelUser(channel->channel_info, user->handle_info);
5517 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
5518 if(!(victim = modcmd_get_handle_info(user, argv[1])))
5519 return 0;
5520 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
5521 {
5522 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
5523 return 0;
5524 }
5525 if((ud->access >= actor->access) && (ud != actor))
5526 {
5527 reply("MSG_USER_OUTRANKED", victim->handle);
5528 return 0;
5529 }
5530 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
5531 override = CMD_LOG_OVERRIDE;
5532 if(ud->info)
5533 free(ud->info);
5534 ud->info = NULL;
5535 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
5536 return 1 | override;
5537 }
5538
5539 static void
5540 resync_channel(struct chanNode *channel)
5541 {
5542 struct mod_chanmode *changes;
5543 struct chanData *cData = channel->channel_info;
5544 unsigned int ii, used;
5545
5546 /* 6 = worst case -ovh+ovh on everyone */
5547 changes = mod_chanmode_alloc(channel->members.used * 6);
5548 for(ii = used = 0; ii < channel->members.used; ++ii)
5549 {
5550 struct modeNode *mn = channel->members.list[ii];
5551 struct userData *uData;
5552
5553 if(IsService(mn->user))
5554 continue;
5555
5556
5557 uData = GetChannelAccess(cData, mn->user->handle_info);
5558
5559 /* If the channel is in no-mode mode, de-mode EVERYONE */
5560 if(cData->chOpts[chAutomode] == 'n')
5561 {
5562 if(mn->modes)
5563 {
5564 changes->args[used].mode = MODE_REMOVE | mn->modes;
5565 changes->args[used++].u.member = mn;
5566 }
5567 }
5568 else /* Give various userlevels their modes.. */
5569 {
5570 if(uData && uData->access >= UL_OP )
5571 {
5572 if(!(mn->modes & MODE_CHANOP))
5573 {
5574 changes->args[used].mode = MODE_CHANOP;
5575 changes->args[used++].u.member = mn;
5576 }
5577 }
5578 else if(uData && uData->access >= UL_HALFOP)
5579 {
5580 if(mn->modes & MODE_CHANOP)
5581 {
5582 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
5583 changes->args[used++].u.member = mn;
5584 }
5585 if(!(mn->modes & MODE_HALFOP))
5586 {
5587 changes->args[used].mode = MODE_HALFOP;
5588 changes->args[used++].u.member = mn;
5589 }
5590 }
5591 else if(uData && uData->access >= UL_PEON )
5592 {
5593 if(mn->modes & MODE_CHANOP)
5594 {
5595 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
5596 changes->args[used++].u.member = mn;
5597 }
5598 if(mn->modes & MODE_HALFOP)
5599 {
5600 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
5601 changes->args[used++].u.member = mn;
5602 }
5603 /* Don't voice peons if were in mode m */
5604 if( cData->chOpts[chAutomode] == 'm')
5605 {
5606 if(mn->modes & MODE_VOICE)
5607 {
5608 changes->args[used].mode = MODE_REMOVE | MODE_VOICE;
5609 changes->args[used++].u.member = mn;
5610 }
5611 }
5612 /* otherwise, make user they do have voice */
5613 else if(!(mn->modes & MODE_VOICE))
5614 {
5615 changes->args[used].mode = MODE_VOICE;
5616 changes->args[used++].u.member = mn;
5617 }
5618 }
5619 else /* They arnt on the userlist.. */
5620 {
5621 /* If we voice everyone, but they dont.. */
5622 if(cData->chOpts[chAutomode] == 'v')
5623 {
5624 /* Remove anything except v */
5625 if(mn->modes & ~MODE_VOICE)
5626 {
5627 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5628 changes->args[used++].u.member = mn;
5629 }
5630 /* Add v */
5631 if(!(mn->modes & MODE_VOICE))
5632 {
5633 changes->args[used].mode = MODE_VOICE;
5634 changes->args[used++].u.member = mn;
5635 }
5636 }
5637 /* If we hop everyone, but they dont.. */
5638 else if(cData->chOpts[chAutomode] == 'h')
5639 {
5640 /* Remove anything except h */
5641 if(mn->modes & ~MODE_HALFOP)
5642 {
5643 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP);
5644 changes->args[used++].u.member = mn;
5645 }
5646 /* Add h */
5647 if(!(mn->modes & MODE_HALFOP))
5648 {
5649 changes->args[used].mode = MODE_HALFOP;
5650 changes->args[used++].u.member = mn;
5651 }
5652 }
5653 /* If we op everyone, but they dont.. */
5654 else if(cData->chOpts[chAutomode] == 'o')
5655 {
5656 /* Remove anything except h */
5657 if(mn->modes & ~MODE_CHANOP)
5658 {
5659 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP);
5660 changes->args[used++].u.member = mn;
5661 }
5662 /* Add h */
5663 if(!(mn->modes & MODE_CHANOP))
5664 {
5665 changes->args[used].mode = MODE_CHANOP;
5666 changes->args[used++].u.member = mn;
5667 }
5668 }
5669 /* they have no excuse for having modes, de-everything them */
5670 else
5671 {
5672 if(mn->modes)
5673 {
5674 changes->args[used].mode = MODE_REMOVE | mn->modes;
5675 changes->args[used++].u.member = mn;
5676 }
5677 }
5678 }
5679 }
5680 }
5681 changes->argc = used;
5682 mod_chanmode_announce(chanserv, channel, changes);
5683 mod_chanmode_free(changes);
5684 }
5685
5686 static CHANSERV_FUNC(cmd_resync)
5687 {
5688 resync_channel(channel);
5689 reply("CSMSG_RESYNCED_USERS", channel->name);
5690 return 1;
5691 }
5692
5693 static CHANSERV_FUNC(cmd_seen)
5694 {
5695 struct userData *uData;
5696 struct handle_info *handle;
5697 char seen[INTERVALLEN];
5698
5699 REQUIRE_PARAMS(2);
5700
5701 if(!irccasecmp(argv[1], chanserv->nick))
5702 {
5703 reply("CSMSG_IS_CHANSERV");
5704 return 1;
5705 }
5706
5707 if(!(handle = get_handle_info(argv[1])))
5708 {
5709 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5710 return 0;
5711 }
5712
5713 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5714 {
5715 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5716 return 0;
5717 }
5718
5719 if(uData->present)
5720 reply("CSMSG_USER_PRESENT", handle->handle);
5721 else if(uData->seen)
5722 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5723 else
5724 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5725
5726 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5727 reply("CSMSG_USER_VACATION", handle->handle);
5728
5729 return 1;
5730 }
5731
5732 static MODCMD_FUNC(cmd_names)
5733 {
5734 struct userNode *targ;
5735 struct userData *targData;
5736 unsigned int ii, pos;
5737 char buf[400];
5738
5739 for(ii=pos=0; ii<channel->members.used; ++ii)
5740 {
5741 targ = channel->members.list[ii]->user;
5742 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5743 if(!targData)
5744 continue;
5745 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5746 {
5747 buf[pos] = 0;
5748 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5749 pos = 0;
5750 }
5751 buf[pos++] = ' ';
5752 if(IsUserSuspended(targData))
5753 buf[pos++] = 's';
5754 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5755 }
5756 buf[pos] = 0;
5757 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5758 reply("CSMSG_END_NAMES", channel->name);
5759 return 1;
5760 }
5761
5762 static int
5763 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5764 {
5765 switch(ntype->visible_type)
5766 {
5767 case NOTE_VIS_ALL: return 1;
5768 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5769 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5770 }
5771 }
5772
5773 static int
5774 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5775 {
5776 struct userData *uData;
5777
5778 switch(ntype->set_access_type)
5779 {
5780 case NOTE_SET_CHANNEL_ACCESS:
5781 if(!user->handle_info)
5782 return 0;
5783 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5784 return 0;
5785 return uData->access >= ntype->set_access.min_ulevel;
5786 case NOTE_SET_CHANNEL_SETTER:
5787 return check_user_level(channel, user, lvlSetters, 1, 0);
5788 case NOTE_SET_PRIVILEGED: default:
5789 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5790 }
5791 }
5792
5793 static CHANSERV_FUNC(cmd_note)
5794 {
5795 struct chanData *cData;
5796 struct note *note;
5797 struct note_type *ntype;
5798
5799 cData = channel->channel_info;
5800 if(!cData)
5801 {
5802 reply("CSMSG_NOT_REGISTERED", channel->name);
5803 return 0;
5804 }
5805
5806 /* If no arguments, show all visible notes for the channel. */
5807 if(argc < 2)
5808 {
5809 dict_iterator_t it;
5810 unsigned int count;
5811
5812 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5813 {
5814 note = iter_data(it);
5815 if(!note_type_visible_to_user(cData, note->type, user))
5816 continue;
5817 if(!count++)
5818 reply("CSMSG_NOTELIST_HEADER", channel->name);
5819 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5820 }
5821 if(count)
5822 reply("CSMSG_NOTELIST_END", channel->name);
5823 else
5824 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5825 }
5826 /* If one argument, show the named note. */
5827 else if(argc == 2)
5828 {
5829 if((note = dict_find(cData->notes, argv[1], NULL))
5830 && note_type_visible_to_user(cData, note->type, user))
5831 {
5832 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5833 }
5834 else if((ntype = dict_find(note_types, argv[1], NULL))
5835 && note_type_visible_to_user(NULL, ntype, user))
5836 {
5837 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5838 return 0;
5839 }
5840 else
5841 {
5842 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5843 return 0;
5844 }
5845 }
5846 /* Assume they're trying to set a note. */
5847 else
5848 {
5849 char *note_text;
5850 ntype = dict_find(note_types, argv[1], NULL);
5851 if(!ntype)
5852 {
5853 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5854 return 0;
5855 }
5856 else if(note_type_settable_by_user(channel, ntype, user))
5857 {
5858 note_text = unsplit_string(argv+2, argc-2, NULL);
5859 if((note = dict_find(cData->notes, argv[1], NULL)))
5860 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5861 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5862 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5863
5864 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5865 {
5866 /* The note is viewable to staff only, so return 0
5867 to keep the invocation from getting logged (or
5868 regular users can see it in !events). */
5869 return 0;
5870 }
5871 }
5872 else
5873 {
5874 reply("CSMSG_NO_ACCESS");
5875 return 0;
5876 }
5877 }
5878 return 1;
5879 }
5880
5881 static CHANSERV_FUNC(cmd_delnote)
5882 {
5883 struct note *note;
5884
5885 REQUIRE_PARAMS(2);
5886 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5887 || !note_type_settable_by_user(channel, note->type, user))
5888 {
5889 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5890 return 0;
5891 }
5892 dict_remove(channel->channel_info->notes, note->type->name);
5893 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5894 return 1;
5895 }
5896
5897 static CHANSERV_FUNC(cmd_last)
5898 {
5899 int numoflines;
5900
5901 REQUIRE_PARAMS(1);
5902
5903 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
5904
5905 if(numoflines < 1 || numoflines > 200)
5906 {
5907 reply("CSMSG_LAST_INVALID");
5908 return 0;
5909 }
5910 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
5911 return 1;
5912 }
5913
5914 static CHANSERV_FUNC(cmd_events)
5915 {
5916 struct logSearch discrim;
5917 struct logReport report;
5918 unsigned int matches, limit;
5919
5920 limit = (argc > 1) ? atoi(argv[1]) : 10;
5921 if(limit < 1 || limit > 200)
5922 limit = 10;
5923
5924 memset(&discrim, 0, sizeof(discrim));
5925 discrim.masks.bot = chanserv;
5926 discrim.masks.channel_name = channel->name;
5927 if(argc > 2)
5928 discrim.masks.command = argv[2];
5929 discrim.limit = limit;
5930 discrim.max_time = INT_MAX;
5931 discrim.severities = 1 << LOG_COMMAND;
5932 report.reporter = chanserv;
5933 report.user = user;
5934 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
5935 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5936 reply("CSMSG_BAR");
5937 matches = log_entry_search(&discrim, log_report_entry, &report);
5938 if(matches)
5939 reply("MSG_MATCH_COUNT", matches);
5940 else
5941 reply("MSG_NO_MATCHES");
5942 return 1;
5943 }
5944
5945 static CHANSERV_FUNC(cmd_say)
5946 {
5947 char *msg;
5948 if(channel)
5949 {
5950 REQUIRE_PARAMS(2);
5951 msg = unsplit_string(argv + 1, argc - 1, NULL);
5952 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5953 }
5954 else if(GetUserH(argv[1]))
5955 {
5956 REQUIRE_PARAMS(3);
5957 msg = unsplit_string(argv + 2, argc - 2, NULL);
5958 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5959 }
5960 else
5961 {
5962 reply("MSG_NOT_TARGET_NAME");
5963 return 0;
5964 }
5965 return 1;
5966 }
5967
5968 static CHANSERV_FUNC(cmd_emote)
5969 {
5970 char *msg;
5971 assert(argc >= 2);
5972 if(channel)
5973 {
5974 /* CTCP is so annoying. */
5975 msg = unsplit_string(argv + 1, argc - 1, NULL);
5976 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5977 }
5978 else if(GetUserH(argv[1]))
5979 {
5980 msg = unsplit_string(argv + 2, argc - 2, NULL);
5981 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5982 }
5983 else
5984 {
5985 reply("MSG_NOT_TARGET_NAME");
5986 return 0;
5987 }
5988 return 1;
5989 }
5990
5991 struct channelList *
5992 chanserv_support_channels(void)
5993 {
5994 return &chanserv_conf.support_channels;
5995 }
5996
5997 static CHANSERV_FUNC(cmd_expire)
5998 {
5999 int channel_count = registered_channels;
6000 expire_channels(NULL);
6001 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
6002 return 1;
6003 }
6004
6005 static void
6006 chanserv_expire_suspension(void *data)
6007 {
6008 struct suspended *suspended = data;
6009 struct chanNode *channel;
6010
6011 if(!suspended->expires || (now < suspended->expires))
6012 suspended->revoked = now;
6013 channel = suspended->cData->channel;
6014 suspended->cData->channel = channel;
6015 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
6016 if(!IsOffChannel(suspended->cData))
6017 {
6018 spamserv_cs_suspend(channel, 0, 0, NULL);
6019 ss_cs_join_channel(channel, 1);
6020 }
6021 }
6022
6023 static CHANSERV_FUNC(cmd_csuspend)
6024 {
6025 struct suspended *suspended;
6026 char reason[MAXLEN];
6027 time_t expiry, duration;
6028 struct userData *uData;
6029
6030 REQUIRE_PARAMS(3);
6031
6032 if(IsProtected(channel->channel_info))
6033 {
6034 reply("CSMSG_SUSPEND_NODELETE", channel->name);
6035 return 0;
6036 }
6037
6038 if(argv[1][0] == '!')
6039 argv[1]++;
6040 else if(IsSuspended(channel->channel_info))
6041 {
6042 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
6043 show_suspension_info(cmd, user, channel->channel_info->suspended);
6044 return 0;
6045 }
6046
6047 if(!strcmp(argv[1], "0"))
6048 expiry = 0;
6049 else if((duration = ParseInterval(argv[1])))
6050 expiry = now + duration;
6051 else
6052 {
6053 reply("MSG_INVALID_DURATION", argv[1]);
6054 return 0;
6055 }
6056
6057 unsplit_string(argv + 2, argc - 2, reason);
6058
6059 suspended = calloc(1, sizeof(*suspended));
6060 suspended->revoked = 0;
6061 suspended->issued = now;
6062 suspended->suspender = strdup(user->handle_info->handle);
6063 suspended->expires = expiry;
6064 suspended->reason = strdup(reason);
6065 suspended->cData = channel->channel_info;
6066 suspended->previous = suspended->cData->suspended;
6067 suspended->cData->suspended = suspended;
6068
6069 if(suspended->expires)
6070 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6071
6072 if(IsSuspended(channel->channel_info))
6073 {
6074 suspended->previous->revoked = now;
6075 if(suspended->previous->expires)
6076 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
6077
6078 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENSION_MODIFIED",
6079 channel->name, suspended->suspender);
6080 }
6081 else
6082 {
6083 /* Mark all users in channel as absent. */
6084 for(uData = channel->channel_info->users; uData; uData = uData->next)
6085 {
6086 if(uData->present)
6087 {
6088 uData->seen = now;
6089 uData->present = 0;
6090 }
6091 }
6092
6093 /* Mark the channel as suspended, then part. */
6094 channel->channel_info->flags |= CHANNEL_SUSPENDED;
6095 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
6096 DelChannelUser(chanserv, channel, suspended->reason, 0);
6097 reply("CSMSG_SUSPENDED", channel->name);
6098 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENDED_BY",
6099 channel->name, suspended->suspender);
6100 }
6101 return 1;
6102 }
6103
6104 static CHANSERV_FUNC(cmd_cunsuspend)
6105 {
6106 struct suspended *suspended;
6107
6108 if(!IsSuspended(channel->channel_info))
6109 {
6110 reply("CSMSG_NOT_SUSPENDED", channel->name);
6111 return 0;
6112 }
6113
6114 suspended = channel->channel_info->suspended;
6115
6116 /* Expire the suspension and join ChanServ to the channel. */
6117 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
6118 chanserv_expire_suspension(suspended);
6119 reply("CSMSG_UNSUSPENDED", channel->name);
6120 global_message_args(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, "CSMSG_UNSUSPENDED_BY",
6121 channel->name, user->handle_info->handle);
6122 return 1;
6123 }
6124
6125 typedef struct chanservSearch
6126 {
6127 char *name;
6128 char *registrar;
6129
6130 time_t unvisited;
6131 time_t registered;
6132
6133 unsigned long flags;
6134 unsigned int limit;
6135 } *search_t;
6136
6137 typedef void (*channel_search_func)(struct chanData *channel, void *data);
6138
6139 static search_t
6140 chanserv_search_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
6141 {
6142 search_t search;
6143 unsigned int i;
6144
6145 search = malloc(sizeof(struct chanservSearch));
6146 memset(search, 0, sizeof(*search));
6147 search->limit = 25;
6148
6149 for(i = 0; i < argc; i++)
6150 {
6151 /* Assume all criteria require arguments. */
6152 if(i == (argc - 1))
6153 {
6154 reply("MSG_MISSING_PARAMS", argv[i]);
6155 goto fail;
6156 }
6157
6158 if(!irccasecmp(argv[i], "name"))
6159 search->name = argv[++i];
6160 else if(!irccasecmp(argv[i], "registrar"))
6161 search->registrar = argv[++i];
6162 else if(!irccasecmp(argv[i], "unvisited"))
6163 search->unvisited = ParseInterval(argv[++i]);
6164 else if(!irccasecmp(argv[i], "registered"))
6165 search->registered = ParseInterval(argv[++i]);
6166 else if(!irccasecmp(argv[i], "flags"))
6167 {
6168 i++;
6169 if(!irccasecmp(argv[i], "nodelete"))
6170 search->flags |= CHANNEL_NODELETE;
6171 else if(!irccasecmp(argv[i], "suspended"))
6172 search->flags |= CHANNEL_SUSPENDED;
6173 else if(!irccasecmp(argv[i], "unreviewed"))
6174 search->flags |= CHANNEL_UNREVIEWED;
6175 else
6176 {
6177 reply("CSMSG_INVALID_CFLAG", argv[i]);
6178 goto fail;
6179 }
6180 }
6181 else if(!irccasecmp(argv[i], "limit"))
6182 search->limit = strtoul(argv[++i], NULL, 10);
6183 else
6184 {
6185 reply("MSG_INVALID_CRITERIA", argv[i]);
6186 goto fail;
6187 }
6188 }
6189
6190 if(search->name && !strcmp(search->name, "*"))
6191 search->name = 0;
6192 if(search->registrar && !strcmp(search->registrar, "*"))
6193 search->registrar = 0;
6194
6195 return search;
6196 fail:
6197 free(search);
6198 return NULL;
6199 }
6200
6201 static int
6202 chanserv_channel_match(struct chanData *channel, search_t search)
6203 {
6204 const char *name = channel->channel->name;
6205 if((search->name && !match_ircglob(name, search->name)) ||
6206 (search->registrar && !channel->registrar) ||
6207 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
6208 (search->unvisited && (now - channel->visited) < search->unvisited) ||
6209 (search->registered && (now - channel->registered) > search->registered) ||
6210 (search->flags && ((search->flags & channel->flags) != search->flags)))
6211 return 0;
6212
6213 return 1;
6214 }
6215
6216 static unsigned int
6217 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
6218 {
6219 struct chanData *channel;
6220 unsigned int matches = 0;
6221
6222 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
6223 {
6224 if(!chanserv_channel_match(channel, search))
6225 continue;
6226 matches++;
6227 smf(channel, data);
6228 }
6229
6230 return matches;
6231 }
6232
6233 static void
6234 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
6235 {
6236 }
6237
6238 static void
6239 search_print(struct chanData *channel, void *data)
6240 {
6241 send_message_type(4, data, chanserv, "%s", channel->channel->name);
6242 }
6243
6244 static CHANSERV_FUNC(cmd_search)
6245 {
6246 search_t search;
6247 unsigned int matches;
6248 channel_search_func action;
6249
6250 REQUIRE_PARAMS(3);
6251
6252 if(!irccasecmp(argv[1], "count"))
6253 action = search_count;
6254 else if(!irccasecmp(argv[1], "print"))
6255 action = search_print;
6256 else
6257 {
6258 reply("CSMSG_ACTION_INVALID", argv[1]);
6259 return 0;
6260 }
6261
6262 search = chanserv_search_create(cmd, user, argc - 2, argv + 2);
6263 if(!search)
6264 return 0;
6265
6266 if(action == search_count)
6267 search->limit = INT_MAX;
6268
6269 if(action == search_print)
6270 {
6271 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6272 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6273 reply("CSMSG_BAR");
6274 }
6275
6276 matches = chanserv_channel_search(search, action, user);
6277
6278 if(matches)
6279 reply("MSG_MATCH_COUNT", matches);
6280 else
6281 reply("MSG_NO_MATCHES");
6282
6283 free(search);
6284 return 1;
6285 }
6286
6287 static CHANSERV_FUNC(cmd_unvisited)
6288 {
6289 struct chanData *cData;
6290 time_t interval = chanserv_conf.channel_expire_delay;
6291 char buffer[INTERVALLEN];
6292 unsigned int limit = 25, matches = 0;
6293
6294 if(argc > 1)
6295 {
6296 interval = ParseInterval(argv[1]);
6297 if(argc > 2)
6298 limit = atoi(argv[2]);
6299 }
6300
6301 intervalString(buffer, interval, user->handle_info);
6302 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
6303
6304 for(cData = channelList; cData && matches < limit; cData = cData->next)
6305 {
6306 if((now - cData->visited) < interval)
6307 continue;
6308
6309 intervalString(buffer, now - cData->visited, user->handle_info);
6310 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
6311 matches++;
6312 }
6313
6314 return 1;
6315 }
6316
6317 static MODCMD_FUNC(chan_opt_unreviewed)
6318 {
6319 struct chanData *cData = channel->channel_info;
6320 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
6321
6322 if(argc > 1)
6323 {
6324 int new_value;
6325
6326 /* The two directions can have different ACLs. */
6327 if(enabled_string(argv[1]))
6328 new_value = 1;
6329 else if(disabled_string(argv[1]))
6330 new_value = 0;
6331 else
6332 {
6333 reply("MSG_INVALID_BINARY", argv[1]);
6334 return 0;
6335 }
6336
6337 if (new_value != value)
6338 {
6339 struct svccmd *subcmd;
6340 char subcmd_name[32];
6341
6342 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
6343 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
6344 if(!subcmd)
6345 {
6346 reply("MSG_COMMAND_DISABLED", subcmd_name);
6347 return 0;
6348 }
6349 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6350 return 0;
6351
6352 if (new_value)
6353 cData->flags |= CHANNEL_UNREVIEWED;
6354 else
6355 {
6356 free(cData->registrar);
6357 cData->registrar = strdup(user->handle_info->handle);
6358 cData->flags &= ~CHANNEL_UNREVIEWED;
6359 }
6360 value = new_value;
6361 }
6362 }
6363
6364 if(value)
6365 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6366 else
6367 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6368 return 1;
6369 }
6370
6371 static MODCMD_FUNC(chan_opt_defaulttopic)
6372 {
6373 if(argc > 1)
6374 {
6375 char *topic;
6376
6377 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
6378 {
6379 reply("CSMSG_TOPIC_LOCKED", channel->name);
6380 return 0;
6381 }
6382
6383 topic = unsplit_string(argv+1, argc-1, NULL);
6384
6385 free(channel->channel_info->topic);
6386 if(topic[0] == '*' && topic[1] == 0)
6387 {
6388 topic = channel->channel_info->topic = NULL;
6389 }
6390 else
6391 {
6392 topic = channel->channel_info->topic = strdup(topic);
6393 if(channel->channel_info->topic_mask
6394 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
6395 reply("CSMSG_TOPIC_MISMATCH", channel->name);
6396 }
6397 SetChannelTopic(channel, chanserv, user, topic ? topic : "", 1);
6398 }
6399
6400 if(channel->channel_info->topic)
6401 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
6402 else
6403 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
6404 return 1;
6405 }
6406
6407 static MODCMD_FUNC(chan_opt_topicmask)
6408 {
6409 if(argc > 1)
6410 {
6411 struct chanData *cData = channel->channel_info;
6412 char *mask;
6413
6414 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
6415 {
6416 reply("CSMSG_TOPIC_LOCKED", channel->name);
6417 return 0;
6418 }
6419
6420 mask = unsplit_string(argv+1, argc-1, NULL);
6421
6422 if(cData->topic_mask)
6423 free(cData->topic_mask);
6424 if(mask[0] == '*' && mask[1] == 0)
6425 {
6426 cData->topic_mask = 0;
6427 }
6428 else
6429 {
6430 cData->topic_mask = strdup(mask);
6431 if(!cData->topic)
6432 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
6433 else if(!match_ircglob(cData->topic, cData->topic_mask))
6434 reply("CSMSG_TOPIC_MISMATCH", channel->name);
6435 }
6436 }
6437
6438 if(channel->channel_info->topic_mask)
6439 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
6440 else
6441 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
6442 return 1;
6443 }
6444
6445 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
6446 {
6447 if(argc > 1)
6448 {
6449 char *greeting = unsplit_string(argv+1, argc-1, NULL);
6450 char *previous;
6451
6452 previous = *data;
6453 if(greeting[0] == '*' && greeting[1] == 0)
6454 *data = NULL;
6455 else
6456 {
6457 unsigned int length = strlen(greeting);
6458 if(length > chanserv_conf.greeting_length)
6459 {
6460 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
6461 return 0;
6462 }
6463 *data = strdup(greeting);
6464 }
6465 if(previous)
6466 free(previous);
6467 }
6468
6469 if(*data)
6470 reply(name, *data);
6471 else
6472 reply(name, user_find_message(user, "MSG_NONE"));
6473 return 1;
6474 }
6475
6476 static MODCMD_FUNC(chan_opt_greeting)
6477 {
6478 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
6479 }
6480
6481 static MODCMD_FUNC(chan_opt_usergreeting)
6482 {
6483 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
6484 }
6485
6486 static MODCMD_FUNC(chan_opt_modes)
6487 {
6488 struct mod_chanmode *new_modes;
6489 char modes[MODELEN];
6490
6491 if(argc > 1)
6492 {
6493 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
6494 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6495 return 0;
6496 }
6497
6498 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
6499 {
6500 reply("CSMSG_NO_ACCESS");
6501 return 0;
6502 }
6503 if(argv[1][0] == '*' && argv[1][1] == 0)
6504 {
6505 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
6506 }
6507 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1,MCP_KEY_FREE|MCP_REGISTERED, 0)))
6508 {
6509 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
6510 return 0;
6511 }
6512 else if(new_modes->argc > 1)
6513 {
6514 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
6515 mod_chanmode_free(new_modes);
6516 return 0;
6517 }
6518 else
6519 {
6520 channel->channel_info->modes = *new_modes;
6521 modcmd_chanmode_announce(new_modes);
6522 mod_chanmode_free(new_modes);
6523 }
6524 }
6525
6526 mod_chanmode_format(&channel->channel_info->modes, modes);
6527 if(modes[0])
6528 reply("CSMSG_SET_MODES", modes);
6529 else
6530 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
6531 return 1;
6532 }
6533
6534 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6535 static int
6536 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6537 {
6538 struct chanData *cData = channel->channel_info;
6539 int value;
6540
6541 if(argc > 1)
6542 {
6543 /* Set flag according to value. */
6544 if(enabled_string(argv[1]))
6545 {
6546 cData->flags |= mask;
6547 value = 1;
6548 }
6549 else if(disabled_string(argv[1]))
6550 {
6551 cData->flags &= ~mask;
6552 value = 0;
6553 }
6554 else
6555 {
6556 reply("MSG_INVALID_BINARY", argv[1]);
6557 return 0;
6558 }
6559 }
6560 else
6561 {
6562 /* Find current option value. */
6563 value = (cData->flags & mask) ? 1 : 0;
6564 }
6565
6566 if(value)
6567 reply(name, user_find_message(user, "MSG_ON"));
6568 else
6569 reply(name, user_find_message(user, "MSG_OFF"));
6570 return 1;
6571 }
6572
6573 static MODCMD_FUNC(chan_opt_nodelete)
6574 {
6575 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
6576 {
6577 reply("MSG_SETTING_PRIVILEGED", argv[0]);
6578 return 0;
6579 }
6580
6581 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
6582 }
6583
6584 static MODCMD_FUNC(chan_opt_dynlimit)
6585 {
6586 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
6587 }
6588
6589 static MODCMD_FUNC(chan_opt_offchannel)
6590 {
6591 struct chanData *cData = channel->channel_info;
6592 int value;
6593
6594 if(argc > 1)
6595 {
6596 /* Set flag according to value. */
6597 if(enabled_string(argv[1]))
6598 {
6599 if(!IsOffChannel(cData))
6600 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
6601 cData->flags |= CHANNEL_OFFCHANNEL;
6602 value = 1;
6603 }
6604 else if(disabled_string(argv[1]))
6605 {
6606 if(IsOffChannel(cData))
6607 {
6608 struct mod_chanmode change;
6609 mod_chanmode_init(&change);
6610 change.argc = 1;
6611 change.args[0].mode = MODE_CHANOP;
6612 change.args[0].u.member = AddChannelUser(chanserv, channel);
6613 mod_chanmode_announce(chanserv, channel, &change);
6614 }
6615 cData->flags &= ~CHANNEL_OFFCHANNEL;
6616 value = 0;
6617 }
6618 else
6619 {
6620 reply("MSG_INVALID_BINARY", argv[1]);
6621 return 0;
6622 }
6623 }
6624 else
6625 {
6626 /* Find current option value. */
6627 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
6628 }
6629
6630 if(value)
6631 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
6632 else
6633 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
6634 return 1;
6635 }
6636
6637 static MODCMD_FUNC(chan_opt_defaults)
6638 {
6639 struct userData *uData;
6640 struct chanData *cData;
6641 const char *confirm;
6642 enum levelOption lvlOpt;
6643 enum charOption chOpt;
6644
6645 cData = channel->channel_info;
6646 uData = GetChannelUser(cData, user->handle_info);
6647 if(!uData || (uData->access < UL_OWNER))
6648 {
6649 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6650 return 0;
6651 }
6652 confirm = make_confirmation_string(uData);
6653 if((argc < 2) || strcmp(argv[1], confirm))
6654 {
6655 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6656 return 0;
6657 }
6658 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6659 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6660 cData->modes = chanserv_conf.default_modes;
6661 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6662 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6663 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6664 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6665 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6666 return 1;
6667 }
6668
6669 static int
6670 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6671 {
6672 struct chanData *cData = channel->channel_info;
6673 struct userData *uData;
6674 unsigned short value;
6675
6676 if(argc > 1)
6677 {
6678 if(!check_user_level(channel, user, option, 1, 1))
6679 {
6680 reply("CSMSG_CANNOT_SET");
6681 return 0;
6682 }
6683 value = user_level_from_name(argv[1], UL_OWNER+1);
6684 if(!value && strcmp(argv[1], "0"))
6685 {
6686 reply("CSMSG_INVALID_ACCESS", argv[1]);
6687 return 0;
6688 }
6689 uData = GetChannelUser(cData, user->handle_info);
6690 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6691 {
6692 reply("CSMSG_BAD_SETLEVEL");
6693 return 0;
6694 }
6695 switch(option)
6696 {
6697 case lvlSetters:
6698 /* This test only applies to owners, since non-owners
6699 * trying to set an option to above their level get caught
6700 * by the CSMSG_BAD_SETLEVEL test above.
6701 */
6702 if(value > uData->access)
6703 {
6704 reply("CSMSG_BAD_SETTERS");
6705 return 0;
6706 }
6707 break;
6708 default:
6709 break;
6710 }
6711 cData->lvlOpts[option] = value;
6712 }
6713 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6714 return argc > 1;
6715 }
6716
6717 static MODCMD_FUNC(chan_opt_enfops)
6718 {
6719 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6720 }
6721
6722 static MODCMD_FUNC(chan_opt_enfhalfops)
6723 {
6724 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
6725 }
6726 static MODCMD_FUNC(chan_opt_enfmodes)
6727 {
6728 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6729 }
6730
6731 static MODCMD_FUNC(chan_opt_enftopic)
6732 {
6733 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6734 }
6735
6736 static MODCMD_FUNC(chan_opt_pubcmd)
6737 {
6738 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6739 }
6740
6741 static MODCMD_FUNC(chan_opt_setters)
6742 {
6743 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6744 }
6745
6746 static MODCMD_FUNC(chan_opt_userinfo)
6747 {
6748 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6749 }
6750
6751 static MODCMD_FUNC(chan_opt_topicsnarf)
6752 {
6753 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6754 }
6755
6756 static MODCMD_FUNC(chan_opt_inviteme)
6757 {
6758 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6759 }
6760
6761 /* TODO: Make look like this when no args are
6762 * given:
6763 * -X3- -------------------------------
6764 * -X3- BanTimeout: Bans are removed:
6765 * -X3- ----- * indicates current -----
6766 * -X3- 0: [*] Never.
6767 * -X3- 1: [ ] After 10 minutes.
6768 * -X3- 2: [ ] After 2 hours.
6769 * -X3- 3: [ ] After 4 hours.
6770 * -X3- 4: [ ] After 24 hours.
6771 * -X3- 5: [ ] After one week.
6772 * -X3- ------------- End -------------
6773 */
6774 static int
6775 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6776 {
6777 struct chanData *cData = channel->channel_info;
6778 int count = charOptions[option].count, index;
6779
6780 if(argc > 1)
6781 {
6782 index = atoi(argv[1]);
6783
6784 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
6785 {
6786 reply("CSMSG_INVALID_NUMERIC", index);
6787 /* Show possible values. */
6788 for(index = 0; index < count; index++)
6789 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6790 return 0;
6791 }
6792
6793 cData->chOpts[option] = charOptions[option].values[index].value;
6794 }
6795 else
6796 {
6797 /* Find current option value. */
6798 find_value:
6799 for(index = 0;
6800 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
6801 index++);
6802 if(index == count)
6803 {
6804 /* Somehow, the option value is corrupt; reset it to the default. */
6805 cData->chOpts[option] = charOptions[option].default_value;
6806 goto find_value;
6807 }
6808 }
6809
6810 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6811 return 1;
6812 }
6813
6814 static MODCMD_FUNC(chan_opt_automode)
6815 {
6816 return channel_multiple_option(chAutomode, CSFUNC_ARGS);
6817 }
6818
6819 static MODCMD_FUNC(chan_opt_protect)
6820 {
6821 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6822 }
6823
6824 static MODCMD_FUNC(chan_opt_toys)
6825 {
6826 return channel_multiple_option(chToys, CSFUNC_ARGS);
6827 }
6828
6829 static MODCMD_FUNC(chan_opt_ctcpreaction)
6830 {
6831 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6832 }
6833
6834 static MODCMD_FUNC(chan_opt_bantimeout)
6835 {
6836 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
6837 }
6838
6839 static MODCMD_FUNC(chan_opt_topicrefresh)
6840 {
6841 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6842 }
6843
6844 static MODCMD_FUNC(chan_opt_resync)
6845 {
6846 return channel_multiple_option(chResync, CSFUNC_ARGS);
6847 }
6848
6849 static struct svccmd_list set_shows_list;
6850
6851 static void
6852 handle_svccmd_unbind(struct svccmd *target) {
6853 unsigned int ii;
6854 for(ii=0; ii<set_shows_list.used; ++ii)
6855 if(target == set_shows_list.list[ii])
6856 set_shows_list.used = 0;
6857 }
6858
6859 static CHANSERV_FUNC(cmd_set)
6860 {
6861 struct svccmd *subcmd;
6862 char buf[MAXLEN];
6863 unsigned int ii;
6864
6865 /* Check if we need to (re-)initialize set_shows_list. */
6866 if(!set_shows_list.used)
6867 {
6868 if(!set_shows_list.size)
6869 {
6870 set_shows_list.size = chanserv_conf.set_shows->used;
6871 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6872 }
6873 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6874 {
6875 const char *name = chanserv_conf.set_shows->list[ii];
6876 sprintf(buf, "%s %s", argv[0], name);
6877 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6878 if(!subcmd)
6879 {
6880 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6881 continue;
6882 }
6883 svccmd_list_append(&set_shows_list, subcmd);
6884 }
6885 }
6886
6887 if(argc < 2)
6888 {
6889 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
6890 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6891 reply("CSMSG_BAR");
6892 for(ii = 0; ii < set_shows_list.used; ii++)
6893 {
6894 subcmd = set_shows_list.list[ii];
6895 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6896 }
6897 reply("CSMSG_CHANNEL_OPTIONS_END");
6898 return 1;
6899 }
6900
6901 sprintf(buf, "%s %s", argv[0], argv[1]);
6902 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6903 if(!subcmd)
6904 {
6905 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6906 return 0;
6907 }
6908 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6909 {
6910 reply("CSMSG_NO_ACCESS");
6911 return 0;
6912 }
6913
6914 argv[0] = "";
6915 argv[1] = buf;
6916
6917 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6918 }
6919
6920 static int
6921 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6922 {
6923 struct userData *uData;
6924
6925 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6926 if(!uData)
6927 {
6928 reply("CSMSG_NOT_USER", channel->name);
6929 return 0;
6930 }
6931
6932 if(argc < 2)
6933 {
6934 /* Just show current option value. */
6935 }
6936 else if(enabled_string(argv[1]))
6937 {
6938 uData->flags |= mask;
6939 }
6940 else if(disabled_string(argv[1]))
6941 {
6942 uData->flags &= ~mask;
6943 }
6944 else
6945 {
6946 reply("MSG_INVALID_BINARY", argv[1]);
6947 return 0;
6948 }
6949
6950 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6951 return 1;
6952 }
6953
6954 static MODCMD_FUNC(user_opt_autoop)
6955 {
6956 struct userData *uData;
6957
6958 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6959 if(!uData)
6960 {
6961 reply("CSMSG_NOT_USER", channel->name);
6962 return 0;
6963 }
6964 if(uData->access < UL_HALFOP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6965 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6966 else
6967 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6968 }
6969
6970 static MODCMD_FUNC(user_opt_autoinvite)
6971 {
6972 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6973 }
6974
6975 static MODCMD_FUNC(user_opt_autojoin)
6976 {
6977 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN, CSFUNC_ARGS);
6978 }
6979
6980 static MODCMD_FUNC(user_opt_info)
6981 {
6982 struct userData *uData;
6983 char *infoline;
6984
6985 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6986
6987 if(!uData)
6988 {
6989 /* If they got past the command restrictions (which require access)
6990 * but fail this test, we have some fool with security override on.
6991 */
6992 reply("CSMSG_NOT_USER", channel->name);
6993 return 0;
6994 }
6995
6996 if(argc > 1)
6997 {
6998 size_t bp;
6999 infoline = unsplit_string(argv + 1, argc - 1, NULL);
7000 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
7001 {
7002 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
7003 return 0;
7004 }
7005 bp = strcspn(infoline, "\001");
7006 if(infoline[bp])
7007 {
7008 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
7009 return 0;
7010 }
7011 if(uData->info)
7012 free(uData->info);
7013 if(infoline[0] == '*' && infoline[1] == 0)
7014 uData->info = NULL;
7015 else
7016 uData->info = strdup(infoline);
7017 }
7018 if(uData->info)
7019 reply("CSMSG_USET_INFO", uData->info);
7020 else
7021 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
7022 return 1;
7023 }
7024
7025 struct svccmd_list uset_shows_list;
7026
7027 static CHANSERV_FUNC(cmd_uset)
7028 {
7029 struct svccmd *subcmd;
7030 char buf[MAXLEN];
7031 unsigned int ii;
7032
7033 /* Check if we need to (re-)initialize uset_shows_list. */
7034 if(!uset_shows_list.used)
7035 {
7036 char *options[] =
7037 {
7038 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7039 };
7040
7041 if(!uset_shows_list.size)
7042 {
7043 uset_shows_list.size = ArrayLength(options);
7044 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
7045 }
7046 for(ii = 0; ii < ArrayLength(options); ii++)
7047 {
7048 const char *name = options[ii];
7049 sprintf(buf, "%s %s", argv[0], name);
7050 subcmd = dict_find(cmd->parent->commands, buf, NULL);
7051 if(!subcmd)
7052 {
7053 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
7054 continue;
7055 }
7056 svccmd_list_append(&uset_shows_list, subcmd);
7057 }
7058 }
7059
7060 if(argc < 2)
7061 {
7062 /* Do this so options are presented in a consistent order. */
7063 reply("CSMSG_USER_OPTIONS");
7064 for(ii = 0; ii < uset_shows_list.used; ii++)
7065 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
7066 return 1;
7067 }
7068
7069 sprintf(buf, "%s %s", argv[0], argv[1]);
7070 subcmd = dict_find(cmd->parent->commands, buf, NULL);
7071 if(!subcmd)
7072 {
7073 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
7074 return 0;
7075 }
7076
7077 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
7078 }
7079
7080 static CHANSERV_FUNC(cmd_giveownership)
7081 {
7082 struct handle_info *new_owner_hi;
7083 struct userData *new_owner;
7084 struct userData *curr_user;
7085 struct userData *invoker;
7086 struct chanData *cData = channel->channel_info;
7087 struct do_not_register *dnr;
7088 const char *confirm;
7089 struct giveownership *giveownership;
7090 unsigned int force, override;
7091 unsigned short co_access, new_owner_old_access;
7092 char transfer_reason[MAXLEN];
7093
7094 REQUIRE_PARAMS(2);
7095 curr_user = GetChannelAccess(cData, user->handle_info);
7096 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
7097
7098 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
7099 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
7100 && (uData->access > 500)
7101 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
7102 || uData->access < 500));
7103
7104
7105 if(!curr_user || (curr_user->access != UL_OWNER))
7106 {
7107 struct userData *owner = NULL;
7108 for(curr_user = channel->channel_info->users;
7109 curr_user;
7110 curr_user = curr_user->next)
7111 {
7112 if(curr_user->access != UL_OWNER)
7113 continue;
7114 if(owner)
7115 {
7116 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
7117 return 0;
7118 }
7119 owner = curr_user;
7120 }
7121 curr_user = owner;
7122 }
7123 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
7124 {
7125 char delay[INTERVALLEN];
7126 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
7127 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
7128 return 0;
7129 }
7130 if (!curr_user) {
7131 reply("CSMSG_NO_OWNER", channel->name);
7132 return 0;
7133 }
7134 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
7135 return 0;
7136 if(new_owner_hi == user->handle_info)
7137 {
7138 reply("CSMSG_NO_TRANSFER_SELF");
7139 return 0;
7140 }
7141 new_owner = GetChannelAccess(cData, new_owner_hi);
7142 if(!new_owner)
7143 {
7144 if(force)
7145 {
7146 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL, 0);
7147 }
7148 else
7149 {
7150 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
7151 return 0;
7152 }
7153 }
7154 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
7155 {
7156 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
7157 return 0;
7158 }
7159 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
7160 if(!IsHelping(user))
7161 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
7162 else
7163 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
7164 return 0;
7165 }
7166
7167 invoker = GetChannelUser(cData, user->handle_info);
7168 if(invoker->access <= UL_OWNER)
7169 {
7170 confirm = make_confirmation_string(curr_user);
7171 if((argc < 3) || strcmp(argv[2], confirm))
7172 {
7173 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
7174 return 0;
7175 }
7176 }
7177
7178 new_owner_old_access = new_owner->access;
7179 if(new_owner->access >= UL_COOWNER)
7180 co_access = new_owner->access;
7181 else
7182 co_access = UL_COOWNER;
7183 new_owner->access = UL_OWNER;
7184 if(curr_user)
7185 curr_user->access = co_access;
7186 cData->ownerTransfer = now;
7187
7188 giveownership = calloc(1, sizeof(*giveownership));
7189 giveownership->issued = now;
7190 giveownership->old_owner = strdup(curr_user->handle->handle);
7191 giveownership->target = strdup(new_owner_hi->handle);
7192 giveownership->target_access = new_owner_old_access;
7193 if(override)
7194 {
7195 if(argc > (2 + force))
7196 {
7197 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
7198 giveownership->reason = strdup(transfer_reason);
7199 }
7200 giveownership->staff_issuer = strdup(user->handle_info->handle);
7201 }
7202
7203 giveownership->previous = channel->channel_info->giveownership;
7204 channel->channel_info->giveownership = giveownership;
7205
7206 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
7207 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_OWNERSHIP_TRANSFERRED",
7208 channel->name, new_owner_hi->handle, user->handle_info->handle);
7209 return 1;
7210 }
7211
7212 static void
7213 chanserv_expire_user_suspension(void *data)
7214 {
7215 struct userData *target = data;
7216
7217 target->expires = 0;
7218 target->flags &= ~USER_SUSPENDED;
7219 }
7220
7221 static CHANSERV_FUNC(cmd_suspend)
7222 {
7223 struct handle_info *hi;
7224 struct userData *self, *real_self, *target;
7225 unsigned int override = 0;
7226 time_t expiry;
7227
7228 REQUIRE_PARAMS(3);
7229 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
7230 self = GetChannelUser(channel->channel_info, user->handle_info);
7231 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
7232 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7233 {
7234 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7235 return 0;
7236 }
7237 if(target->access >= self->access)
7238 {
7239 reply("MSG_USER_OUTRANKED", hi->handle);
7240 return 0;
7241 }
7242 if(target->flags & USER_SUSPENDED)
7243 {
7244 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
7245 return 0;
7246 }
7247 if(target->present)
7248 {
7249 target->present = 0;
7250 target->seen = now;
7251 }
7252 if(!strcmp(argv[2], "0"))
7253 expiry = 0;
7254 else
7255 {
7256 unsigned int duration;
7257 if(!(duration = ParseInterval(argv[2])))
7258 {
7259 reply("MSG_INVALID_DURATION", argv[2]);
7260 return 0;
7261 }
7262 expiry = now + duration;
7263 }
7264
7265 target->expires = expiry;
7266
7267 if(target->expires)
7268 timeq_add(target->expires, chanserv_expire_user_suspension, target);
7269
7270 if(!real_self || target->access >= real_self->access)
7271 override = CMD_LOG_OVERRIDE;
7272 target->flags |= USER_SUSPENDED;
7273 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
7274 return 1 | override;
7275 }
7276
7277 static CHANSERV_FUNC(cmd_unsuspend)
7278 {
7279 struct handle_info *hi;
7280 struct userData *self, *real_self, *target;
7281 unsigned int override = 0;
7282
7283 REQUIRE_PARAMS(2);
7284 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
7285 self = GetChannelUser(channel->channel_info, user->handle_info);
7286 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
7287 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7288 {
7289 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7290 return 0;
7291 }
7292 if(target->access >= self->access)
7293 {
7294 reply("MSG_USER_OUTRANKED", hi->handle);
7295 return 0;
7296 }
7297 if(!(target->flags & USER_SUSPENDED))
7298 {
7299 reply("CSMSG_NOT_SUSPENDED", hi->handle);
7300 return 0;
7301 }
7302 if(!real_self || target->access >= real_self->access)
7303 override = CMD_LOG_OVERRIDE;
7304 target->flags &= ~USER_SUSPENDED;
7305 scan_user_presence(target, NULL);
7306 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
7307 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
7308 return 1 | override;
7309 }
7310
7311 static MODCMD_FUNC(cmd_deleteme)
7312 {
7313 struct handle_info *hi;
7314 struct userData *target;
7315 const char *confirm_string;
7316 unsigned short access;
7317 char *channel_name;
7318
7319 hi = user->handle_info;
7320 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7321 {
7322 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7323 return 0;
7324 }
7325 if(target->access == UL_OWNER)
7326 {
7327 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
7328 return 0;
7329 }
7330 confirm_string = make_confirmation_string(target);
7331 if((argc < 2) || strcmp(argv[1], confirm_string))
7332 {
7333 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
7334 return 0;
7335 }
7336 access = target->access;
7337 channel_name = strdup(channel->name);
7338 del_channel_user(target, 1);
7339 reply("CSMSG_DELETED_YOU", access, channel_name);
7340 free(channel_name);
7341 return 1;
7342 }
7343
7344 static void
7345 chanserv_refresh_topics(UNUSED_ARG(void *data))
7346 {
7347 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
7348 struct chanData *cData;
7349 char opt;
7350
7351 for(cData = channelList; cData; cData = cData->next)
7352 {
7353 if(IsSuspended(cData))
7354 continue;
7355 opt = cData->chOpts[chTopicRefresh];
7356 if(opt == 'n')
7357 continue;
7358 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7359 continue;
7360 if(cData->topic)
7361 SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1);
7362 cData->last_refresh = refresh_num;
7363 }
7364 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7365 }
7366
7367 static void
7368 chanserv_auto_resync(UNUSED_ARG(void *data))
7369 {
7370 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
7371 struct chanData *cData;
7372 char opt;
7373
7374 for(cData = channelList; cData; cData = cData->next)
7375 {
7376 if(IsSuspended(cData)) continue;
7377 opt = cData->chOpts[chResync];
7378 if(opt == 'n') continue;
7379 if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
7380 resync_channel(cData->channel);
7381 cData->last_resync = refresh_num;
7382 }
7383 timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
7384 }
7385
7386 static CHANSERV_FUNC(cmd_unf)
7387 {
7388 if(channel)
7389 {
7390 char response[MAXLEN];
7391 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7392 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7393 irc_privmsg(cmd->parent->bot, channel->name, response);
7394 }
7395 else
7396 reply("CSMSG_UNF_RESPONSE");
7397 return 1;
7398 }
7399
7400 static CHANSERV_FUNC(cmd_ping)
7401 {
7402 if(channel)
7403 {
7404 char response[MAXLEN];
7405 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7406 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7407 irc_privmsg(cmd->parent->bot, channel->name, response);
7408 }
7409 else
7410 reply("CSMSG_PING_RESPONSE");
7411 return 1;
7412 }
7413
7414 static CHANSERV_FUNC(cmd_wut)
7415 {
7416 if(channel)
7417 {
7418 char response[MAXLEN];
7419 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7420 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7421 irc_privmsg(cmd->parent->bot, channel->name, response);
7422 }
7423 else
7424 reply("CSMSG_WUT_RESPONSE");
7425 return 1;
7426 }
7427
7428 #ifdef lame8ball
7429 static CHANSERV_FUNC(cmd_8ball)
7430 {
7431 unsigned int i, j, accum;
7432 const char *resp;
7433
7434 REQUIRE_PARAMS(2);
7435 accum = 0;
7436 for(i=1; i<argc; i++)
7437 for(j=0; argv[i][j]; j++)
7438 accum = (accum << 5) - accum + toupper(argv[i][j]);
7439 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7440 if(channel)
7441 {
7442 char response[MAXLEN];
7443 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
7444 irc_privmsg(cmd->parent->bot, channel->name, response);
7445 }
7446 else
7447 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7448 return 1;
7449 }
7450
7451 #else /* Use cool 8ball instead */
7452
7453 void eightball(char *outcome, int method, unsigned int seed)
7454 {
7455 int answer = 0;
7456
7457 #define NUMOFCOLORS 18
7458 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
7459 "white", "black", "grey", "brown",
7460 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7461 "fuchsia","turquoise","magenta", "cyan"};
7462 #define NUMOFLOCATIONS 50
7463 char balllocations[50][55] = {
7464 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7465 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7466 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7467 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7468 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7469 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7470 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7471 "your bra", "your hair", "your bed", "the couch", "the wall",
7472 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7473 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7474 #define NUMOFPREPS 15
7475 char ballpreps[50][50] = {
7476 "Near", "Somewhere near", "In", "In", "In",
7477 "In", "Hiding in", "Under", "Next to", "Over",
7478 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7479 #define NUMOFNUMS 34
7480 char ballnums[50][50] = {
7481 "A hundred", "A thousand", "A few", "42",
7482 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7483 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7484 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7485 };
7486 #define NUMOFMULTS 8
7487 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7488
7489 /* Method:
7490 * 0: normal (Not used in x3)
7491 * 1: color
7492 * 2: where is
7493 * 3: how many
7494 */
7495
7496 srand(seed);
7497 if (method == 1) /* A Color */
7498 {
7499 char tmp[MAXLEN];
7500
7501 answer = (rand() % 12); /* Make sure this is the # of entries */
7502 switch(answer)
7503 {
7504 case 0: strcpy(tmp, "Very bright %s, I'd say.");
7505 break;
7506 case 1: strcpy(tmp, "Sort of a light %s color.");
7507 break;
7508 case 2: strcpy(tmp, "Dark and dreary %s.");
7509 break;
7510 case 3: strcpy(tmp, "Quite a pale shade of %s.");
7511 break;
7512 case 4: strcpy(tmp, "A gross kind of mucky %s.");
7513 break;
7514 case 5: strcpy(tmp, "Brilliant whiteish %s.");
7515 break;
7516 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
7517 break;
7518 case 10: strcpy(tmp, "Solid %s.");
7519 break;
7520 case 11: strcpy(tmp, "Transparent %s.");
7521 break;
7522 default: strcpy(outcome, "An invalid random number was generated.");
7523 return;
7524 }
7525 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
7526 return;
7527 }
7528 else if (method == 2) /* Location */
7529 {
7530 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
7531 }
7532 else if (method == 3) /* Number of ___ */
7533 {
7534 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
7535 }
7536 else
7537 {
7538 //Debug(DBGWARNING, "Error in 8ball.");
7539 }
7540 return;
7541 }
7542
7543 static CHANSERV_FUNC(cmd_8ball)
7544 {
7545 char *word1, *word2, *word3;
7546 static char eb[MAXLEN];
7547 unsigned int accum, i, j;
7548
7549 REQUIRE_PARAMS(2);
7550 accum = 0;
7551 for(i=1; i<argc; i++)
7552 for(j=0; argv[i][j]; j++)
7553 accum = (accum << 5) - accum + toupper(argv[i][j]);
7554
7555 accum += time(NULL)/3600;
7556 word1 = argv[1];
7557 word2 = argc>2?argv[2]:"";
7558 word3 = argc>3?argv[3]:"";
7559
7560 /*** COLOR *****/
7561 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
7562 eightball(eb, 1, accum);
7563 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
7564 eightball(eb, 1, accum);
7565 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
7566 eightball(eb, 1, accum);
7567 /*** LOCATION *****/
7568 else if(
7569 (
7570 word2 &&
7571 (
7572 (strcasecmp(word1, "where") == 0) &&
7573 (strcasecmp(word2, "is") == 0)
7574 )
7575 ) ||
7576 (
7577 strcasecmp(word1, "where's") == 0
7578 )
7579 )
7580 eightball(eb, 2, accum);
7581 /*** NUMBER *****/
7582 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
7583 eightball(eb, 3, accum);
7584 /*** GENERIC *****/
7585 else
7586 {
7587 /* Generic 8ball question.. so pull from x3.conf srvx style */
7588 const char *resp;
7589
7590 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7591 if(channel)
7592 {
7593 char response[MAXLEN];
7594 sprintf(response, "\002%s\002: %s", user->nick, resp);
7595 irc_privmsg(cmd->parent->bot, channel->name, response);
7596 }
7597 else
7598 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7599 return 1;
7600 }
7601
7602 if(channel)
7603 {
7604 char response[MAXLEN];
7605 sprintf(response, "\002%s\002: %s", user->nick, eb);
7606 irc_privmsg(cmd->parent->bot, channel->name, response);
7607 }
7608 else
7609 send_message_type(4, user, cmd->parent->bot, "%s", eb);
7610 return 1;
7611 }
7612 #endif
7613
7614 static CHANSERV_FUNC(cmd_d)
7615 {
7616 unsigned long sides, count, modifier, ii, total;
7617 char response[MAXLEN], *sep;
7618 const char *fmt;
7619
7620 REQUIRE_PARAMS(2);
7621 if((count = strtoul(argv[1], &sep, 10)) < 1)
7622 goto no_dice;
7623 if(sep[0] == 0)
7624 {
7625 if(count == 1)
7626 goto no_dice;
7627 sides = count;
7628 count = 1;
7629 modifier = 0;
7630 }
7631 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7632 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7633 {
7634 if(sep[0] == 0)
7635 modifier = 0;
7636 else if((sep[0] == '-') && isdigit(sep[1]))
7637 modifier = strtoul(sep, NULL, 10);
7638 else if((sep[0] == '+') && isdigit(sep[1]))
7639 modifier = strtoul(sep+1, NULL, 10);
7640 else
7641 goto no_dice;
7642 }
7643 else
7644 {
7645 no_dice:
7646 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7647 return 0;
7648 }
7649 if(count > 10)
7650 {
7651 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7652 return 0;
7653 }
7654 for(total = ii = 0; ii < count; ++ii)
7655 total += (rand() % sides) + 1;
7656 total += modifier;
7657
7658 if((count > 1) || modifier)
7659 {
7660 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7661 sprintf(response, fmt, total, count, sides, modifier);
7662 }
7663 else
7664 {
7665 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7666 sprintf(response, fmt, total, sides);
7667 }
7668 if(channel)
7669 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7670 else
7671 send_message_type(4, user, cmd->parent->bot, "%s", response);
7672 return 1;
7673 }
7674
7675 static CHANSERV_FUNC(cmd_huggle)
7676 {
7677 /* CTCP must be via PRIVMSG, never notice */
7678 if(channel)
7679 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7680 else
7681 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7682 return 1;
7683 }
7684
7685 static CHANSERV_FUNC(cmd_calc)
7686 {
7687 char response[MAXLEN];
7688
7689 REQUIRE_PARAMS(2);
7690 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
7691
7692 if(channel)
7693 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7694 else
7695 send_message_type(4, user, cmd->parent->bot, "%s", response);
7696 return 1;
7697 }
7698
7699 static CHANSERV_FUNC(cmd_reply)
7700 {
7701
7702 REQUIRE_PARAMS(2);
7703 unsplit_string(argv + 1, argc - 1, NULL);
7704
7705 if(channel)
7706 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, unsplit_string(argv + 1, argc - 1, NULL));
7707 else
7708 send_message_type(4, user, cmd->parent->bot, "%s", unsplit_string(argv + 1, argc - 1, NULL));
7709 return 1;
7710 }
7711
7712 static void
7713 chanserv_adjust_limit(void *data)
7714 {
7715 struct mod_chanmode change;
7716 struct chanData *cData = data;
7717 struct chanNode *channel = cData->channel;
7718 unsigned int limit;
7719
7720 if(IsSuspended(cData))
7721 return;
7722
7723 cData->limitAdjusted = now;
7724 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7725 if(cData->modes.modes_set & MODE_LIMIT)
7726 {
7727 if(limit > cData->modes.new_limit)
7728 limit = cData->modes.new_limit;
7729 else if(limit == cData->modes.new_limit)
7730 return;
7731 }
7732
7733 mod_chanmode_init(&change);
7734 change.modes_set = MODE_LIMIT;
7735 change.new_limit = limit;
7736 mod_chanmode_announce(chanserv, channel, &change);
7737 }
7738
7739 static void
7740 handle_new_channel(struct chanNode *channel)
7741 {
7742 struct chanData *cData;
7743
7744 if(!(cData = channel->channel_info))
7745 return;
7746
7747 if(cData->modes.modes_set || cData->modes.modes_clear)
7748 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7749
7750 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7751 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
7752 }
7753
7754 int
7755 trace_check_bans(struct userNode *user, struct chanNode *chan)
7756 {
7757 struct banData *bData;
7758 struct mod_chanmode *change;
7759
7760 change = find_matching_bans(&chan->banlist, user, NULL);
7761 if (change)
7762 return 1;
7763
7764 /* lamer list */
7765 if (chan->channel_info) {
7766 for(bData = chan->channel_info->bans; bData; bData = bData->next) {
7767
7768 if(!user_matches_glob(user, bData->mask, MATCH_USENICK))
7769 continue;
7770
7771 if(bData)
7772 return 1;
7773 }
7774 }
7775
7776 return 0;
7777 }
7778
7779 int
7780 check_bans(struct userNode *user, const char *channel)
7781 {
7782 struct chanNode *chan;
7783 struct mod_chanmode change;
7784 struct chanData *cData;
7785 struct banData *bData;
7786
7787 if (!(chan = GetChannel(channel)))
7788 return 0;
7789
7790 if(!(cData = chan->channel_info))
7791 return 0;
7792
7793 mod_chanmode_init(&change);
7794 change.argc = 1;
7795
7796 if(chan->banlist.used < MAXBANS)
7797 {
7798 /* Not joining through a ban. */
7799 for(bData = cData->bans;
7800 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7801 bData = bData->next);
7802
7803 if(bData)
7804 {
7805 char kick_reason[MAXLEN];
7806 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7807
7808 bData->triggered = now;
7809 if(bData != cData->bans)
7810 {
7811 /* Shuffle the ban to the head of the list. */
7812 if(bData->next)
7813 bData->next->prev = bData->prev;
7814 if(bData->prev)
7815 bData->prev->next = bData->next;
7816
7817 bData->prev = NULL;
7818 bData->next = cData->bans;
7819
7820 if(cData->bans)
7821 cData->bans->prev = bData;
7822
7823 cData->bans = bData;
7824 }
7825
7826 change.args[0].mode = MODE_BAN;
7827 change.args[0].u.hostmask = bData->mask;
7828 mod_chanmode_announce(chanserv, chan, &change);
7829 KickChannelUser(user, chan, chanserv, kick_reason);
7830 return 1;
7831 }
7832 }
7833 return 0;
7834 }
7835
7836 int
7837 channel_user_is_exempt(struct userNode *user, struct chanNode *channel)
7838 {
7839 unsigned int ii;
7840 for(ii = 0; ii < channel->exemptlist.used; ii++)
7841 {
7842 if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK))
7843 return true;
7844 }
7845 return false;
7846 }
7847
7848
7849 /* Welcome to my worst nightmare. Warning: Read (or modify)
7850 the code below at your own risk. */
7851 static int
7852 handle_join(struct modeNode *mNode)
7853 {
7854 struct mod_chanmode change;
7855 struct userNode *user = mNode->user;
7856 struct chanNode *channel = mNode->channel;
7857 struct chanData *cData;
7858 struct userData *uData = NULL;
7859 struct banData *bData;
7860 struct handle_info *handle;
7861 unsigned int modes = 0, info = 0;
7862 char *greeting;
7863
7864 if(IsLocal(user) || !channel || !channel->channel_info || IsSuspended(channel->channel_info))
7865 return 0;
7866
7867 cData = channel->channel_info;
7868 if(channel->members.used > cData->max)
7869 cData->max = channel->members.used;
7870
7871 #ifdef notdef
7872 /* Check for bans. If they're joining through a ban, one of two
7873 * cases applies:
7874 * 1: Join during a netburst, by riding the break. Kick them
7875 * unless they have ops or voice in the channel.
7876 * 2: They're allowed to join through the ban (an invite in
7877 * ircu2.10, or a +e on Hybrid, or something).
7878 * If they're not joining through a ban, and the banlist is not
7879 * full, see if they're on the banlist for the channel. If so,
7880 * kickban them.
7881 */
7882 if(user->uplink->burst && !mNode->modes)
7883 {
7884 unsigned int ii;
7885 for(ii = 0; ii < channel->banlist.used; ii++)
7886 {
7887 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7888 {
7889 /* Riding a netburst. Naughty. */
7890 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7891 return 1;
7892 }
7893 }
7894 }
7895 #endif
7896
7897 if(user->handle_info)
7898 {
7899 handle = user->handle_info;
7900 if(handle)
7901 {
7902 uData = GetTrueChannelAccess(cData, handle);
7903 }
7904 }
7905
7906
7907 mod_chanmode_init(&change);
7908 change.argc = 1;
7909
7910 /* TODO: maybe only people above inviteme level? -Rubin */
7911 /* We don't kick people with access */
7912 if(!uData && !channel_user_is_exempt(user, channel))
7913 {
7914 if(channel->banlist.used < MAXBANS)
7915 {
7916 /* Not joining through a ban. */
7917 for(bData = cData->bans;
7918 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7919 bData = bData->next);
7920
7921 if(bData)
7922 {
7923 char kick_reason[MAXLEN];
7924 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7925
7926 bData->triggered = now;
7927 if(bData != cData->bans)
7928 {
7929 /* Shuffle the ban to the head of the list. */
7930 if(bData->next)
7931 bData->next->prev = bData->prev;
7932 if(bData->prev)
7933 bData->prev->next = bData->next;
7934
7935 bData->prev = NULL;
7936 bData->next = cData->bans;
7937
7938 if(cData->bans)
7939 cData->bans->prev = bData;
7940 cData->bans = bData;
7941 }
7942
7943 change.args[0].mode = MODE_BAN;
7944 change.args[0].u.hostmask = bData->mask;
7945 mod_chanmode_announce(chanserv, channel, &change);
7946 KickChannelUser(user, channel, chanserv, kick_reason);
7947 return 1;
7948 }
7949 }
7950 }
7951
7952 /* ChanServ will not modify the limits in join-flooded channels.
7953 It will also skip DynLimit processing when the user (or srvx)
7954 is bursting in, because there are likely more incoming. */
7955 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7956 && !user->uplink->burst
7957 && !channel->join_flooded
7958 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7959 {
7960 /* The user count has begun "bumping" into the channel limit,
7961 so set a timer to raise the limit a bit. Any previous
7962 timers are removed so three incoming users within the delay
7963 results in one limit change, not three. */
7964
7965 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7966 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7967 }
7968
7969 /* Give automodes exept during join-floods */
7970 if(!channel->join_flooded)
7971 {
7972 if(cData->chOpts[chAutomode] == 'v')
7973 modes |= MODE_VOICE;
7974 else if(cData->chOpts[chAutomode] == 'h')
7975 modes |= MODE_HALFOP;
7976 else if(cData->chOpts[chAutomode] == 'o')
7977 modes |= MODE_CHANOP;
7978 }
7979
7980 greeting = cData->greeting;
7981 if(user->handle_info)
7982 {
7983 /* handle = user->handle_info; */
7984
7985 if(IsHelper(user) && !IsHelping(user))
7986 {
7987 unsigned int ii;
7988 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7989 {
7990 if(channel == chanserv_conf.support_channels.list[ii])
7991 {
7992 HANDLE_SET_FLAG(user->handle_info, HELPING);
7993 break;
7994 }
7995 }
7996 }
7997
7998 /* uData = GetTrueChannelAccess(cData, handle); */
7999 if(uData && !IsUserSuspended(uData))
8000 {
8001 /* non users getting automodes are handled above. */
8002 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
8003 {
8004 /* just op everyone with access */
8005 if(uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
8006 modes |= MODE_VOICE;
8007 /* or do their access level */
8008 else if(uData->access >= UL_OP )
8009 modes |= MODE_CHANOP;
8010 else if(uData->access >= UL_HALFOP )
8011 modes |= MODE_HALFOP;
8012 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
8013 modes |= MODE_VOICE;
8014 }
8015 if(uData->access >= UL_PRESENT)
8016 cData->visited = now;
8017 if(cData->user_greeting)
8018 greeting = cData->user_greeting;
8019 if(uData->info
8020 && (uData->access >= cData->lvlOpts[lvlUserInfo])
8021 && ((now - uData->seen) >= chanserv_conf.info_delay)
8022 && !uData->present)
8023 info = 1;
8024 uData->seen = now;
8025 uData->present = 1;
8026 }
8027 }
8028
8029 /* If user joining normally (not during burst), apply op or voice,
8030 * and send greeting/userinfo as appropriate.
8031 */
8032 if(!user->uplink->burst)
8033 {
8034 if(modes)
8035 {
8036 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8037 if(modes & MODE_CHANOP) {
8038 modes &= ~MODE_HALFOP;
8039 modes &= ~MODE_VOICE;
8040 }
8041 */
8042 change.args[0].mode = modes;
8043 change.args[0].u.member = mNode;
8044 mod_chanmode_announce(chanserv, channel, &change);
8045 }
8046 if(greeting)
8047 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
8048 if(uData && info)
8049 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
8050 }
8051 return 0;
8052 }
8053
8054 static void
8055 chanserv_autojoin_channels(struct userNode *user)
8056 {
8057 struct userData *channel;
8058
8059 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
8060 {
8061 struct chanNode *cn;
8062 struct modeNode *mn;
8063
8064 if(IsUserSuspended(channel)
8065 || IsSuspended(channel->channel)
8066 || !(cn = channel->channel->channel))
8067 continue;
8068
8069 mn = GetUserMode(cn, user);
8070 if(!mn)
8071 {
8072 if(!IsUserSuspended(channel)
8073 && IsUserAutoJoin(channel)
8074 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
8075 && !self->burst
8076 && !user->uplink->burst)
8077 irc_svsjoin(chanserv, user, cn);
8078 }
8079 }
8080 }
8081
8082 static void
8083 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
8084 {
8085 struct mod_chanmode change;
8086 struct userData *channel;
8087 unsigned int ii, jj, i;
8088
8089 if(!user->handle_info)
8090 return;
8091
8092 mod_chanmode_init(&change);
8093 change.argc = 1;
8094 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
8095 {
8096 struct chanNode *cn;
8097 struct chanData *cData;
8098 struct modeNode *mn;
8099 if(IsUserSuspended(channel)
8100 || IsSuspended(channel->channel)
8101 || !(cn = channel->channel->channel))
8102 continue;
8103
8104 cData = cn->channel_info;
8105 mn = GetUserMode(cn, user);
8106 if(!mn)
8107 {
8108 if(!IsUserSuspended(channel)
8109 && IsUserAutoInvite(channel)
8110 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
8111 && !self->burst
8112 && !user->uplink->burst)
8113 irc_invite(chanserv, user, cn);
8114 continue;
8115 }
8116
8117 if(channel->access >= UL_PRESENT)
8118 channel->channel->visited = now;
8119
8120 if(IsUserAutoOp(channel) && cData->chOpts[chAutomode] != 'n')
8121 {
8122 if(channel->access >= UL_OP )
8123 change.args[0].mode = MODE_CHANOP;
8124 else if(channel->access >= UL_HALFOP )
8125 change.args[0].mode = MODE_HALFOP;
8126 else if(channel->access >= UL_PEON )
8127 change.args[0].mode = MODE_VOICE;
8128 else
8129 change.args[0].mode = 0;
8130 change.args[0].u.member = mn;
8131 if(change.args[0].mode)
8132 mod_chanmode_announce(chanserv, cn, &change);
8133 }
8134
8135 channel->seen = now;
8136 channel->present = 1;
8137 }
8138
8139 for(ii = 0; ii < user->channels.used; ++ii)
8140 {
8141 struct chanNode *channel = user->channels.list[ii]->channel;
8142 struct banData *ban;
8143
8144 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
8145 || !channel->channel_info
8146 || IsSuspended(channel->channel_info))
8147 continue;
8148 if(protect_user(user, chanserv, channel->channel_info, true))
8149 continue;
8150 for(jj = 0; jj < channel->banlist.used; ++jj)
8151 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
8152 break;
8153 if(jj < channel->banlist.used)
8154 continue;
8155 for(ban = channel->channel_info->bans; ban; ban = ban->next)
8156 {
8157 char kick_reason[MAXLEN];
8158 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
8159 continue;
8160 change.args[0].mode = MODE_BAN;
8161 change.args[0].u.hostmask = ban->mask;
8162 mod_chanmode_announce(chanserv, channel, &change);
8163 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
8164 KickChannelUser(user, channel, chanserv, kick_reason);
8165 ban->triggered = now;
8166 break;
8167 }
8168 }
8169
8170 if(IsSupportHelper(user))
8171 {
8172 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8173 {
8174 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
8175 {
8176 HANDLE_SET_FLAG(user->handle_info, HELPING);
8177 break;
8178 }
8179 }
8180 }
8181
8182 if (user->handle_info->ignores->used) {
8183 for (i=0; i < user->handle_info->ignores->used; i++) {
8184 irc_silence(user, user->handle_info->ignores->list[i], 1);
8185 }
8186 }
8187
8188 if (user->handle_info->epithet)
8189 irc_swhois(chanserv, user, user->handle_info->epithet);
8190
8191 /* process autojoin channels 5 seconds later as this sometimes
8192 happens before autohide */
8193 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8194 chanserv_autojoin_channels(user);
8195 }
8196
8197 static void
8198 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
8199 {
8200 struct chanData *cData;
8201 struct userData *uData;
8202
8203 cData = mn->channel->channel_info;
8204 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
8205 return;
8206
8207 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
8208 {
8209 /* Allow for a bit of padding so that the limit doesn't
8210 track the user count exactly, which could get annoying. */
8211 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
8212 {
8213 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8214 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8215 }
8216 }
8217
8218 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
8219 {
8220 scan_user_presence(uData, mn->user);
8221 uData->seen = now;
8222 if (uData->access >= UL_PRESENT)
8223 cData->visited = now;
8224 }
8225
8226 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
8227 {
8228 unsigned int ii, jj;
8229 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8230 {
8231 for(jj = 0; jj < mn->user->channels.used; ++jj)
8232 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
8233 break;
8234 if(jj < mn->user->channels.used)
8235 break;
8236 }
8237 if(ii == chanserv_conf.support_channels.used)
8238 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
8239 }
8240 }
8241
8242 static void
8243 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
8244 {
8245 struct userData *uData;
8246
8247 if(!channel->channel_info || !kicker || IsService(kicker)
8248 || (kicker == victim) || IsSuspended(channel->channel_info)
8249 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
8250 return;
8251
8252 if(protect_user(victim, kicker, channel->channel_info, false))
8253 {
8254 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
8255 KickChannelUser(kicker, channel, chanserv, reason);
8256 }
8257
8258 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
8259 uData->seen = now;
8260 }
8261
8262 static int
8263 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
8264 {
8265 struct chanData *cData;
8266
8267 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
8268 return 0;
8269
8270 cData = channel->channel_info;
8271 if(bad_topic(channel, user, channel->topic))
8272 { /* User doesnt have privs to set topics. Undo it */
8273 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
8274 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
8275 return 1;
8276 }
8277 /* If there is a topic mask set, and the new topic doesnt match,
8278 * set the topic to mask + new_topic */
8279 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
8280 {
8281 char new_topic[TOPICLEN+1];
8282 conform_topic(cData->topic_mask, channel->topic, new_topic);
8283 if(*new_topic)
8284 {
8285 SetChannelTopic(channel, chanserv, user, new_topic, 1);
8286 /* and fall through to topicsnarf code below.. */
8287 }
8288 else /* Topic couldnt fit into mask, was too long */
8289 {
8290 SetChannelTopic(channel, chanserv, user, old_topic, 1);
8291 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
8292 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
8293 return 1;
8294 }
8295 }
8296 /* With topicsnarf, grab the topic and save it as the default topic. */
8297 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
8298 {
8299 free(cData->topic);
8300 cData->topic = strdup(channel->topic);
8301 }
8302 return 0;
8303 }
8304
8305 static void
8306 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
8307 {
8308 struct mod_chanmode *bounce = NULL;
8309 unsigned int bnc, ii;
8310 char deopped = 0;
8311
8312 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
8313 return;
8314
8315 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
8316 && mode_lock_violated(&channel->channel_info->modes, change))
8317 {
8318 char correct[MAXLEN];
8319 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
8320 mod_chanmode_format(&channel->channel_info->modes, correct);
8321 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
8322 }
8323 for(ii = bnc = 0; ii < change->argc; ++ii)
8324 {
8325 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
8326 {
8327 const struct userNode *victim = change->args[ii].u.member->user;
8328 if(!protect_user(victim, user, channel->channel_info, false))
8329 continue;
8330 if(!bounce)
8331 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8332 if(!deopped)
8333 {
8334 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
8335 bounce->args[bnc].u.member = GetUserMode(channel, user);
8336 if(bounce->args[bnc].u.member)
8337 bnc++;
8338 deopped = 1;
8339 }
8340 bounce->args[bnc].mode = MODE_CHANOP;
8341 bounce->args[bnc].u.member = change->args[ii].u.member;
8342 bnc++;
8343 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
8344 }
8345 else if(change->args[ii].mode & MODE_CHANOP)
8346 {
8347 const struct userNode *victim = change->args[ii].u.member->user;
8348 if(IsService(victim) || validate_op(NULL, user, channel, (struct userNode*)victim))
8349 continue;
8350 if(!bounce)
8351 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8352 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
8353 bounce->args[bnc].u.member = change->args[ii].u.member;
8354 bnc++;
8355 }
8356 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
8357 {
8358 const char *ban = change->args[ii].u.hostmask;
8359 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
8360 continue;
8361 if(!bounce)
8362 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8363 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
8364 bounce->args[bnc].u.hostmask = strdup(ban);
8365 bnc++;
8366 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
8367 }
8368 }
8369 if(bounce)
8370 {
8371 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
8372 mod_chanmode_announce(chanserv, channel, bounce);
8373 for(ii = 0; ii < change->argc; ++ii)
8374 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
8375 free((char*)bounce->args[ii].u.hostmask);
8376 mod_chanmode_free(bounce);
8377 }
8378 }
8379
8380 static void
8381 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
8382 {
8383 struct chanNode *channel;
8384 struct banData *bData;
8385 struct mod_chanmode change;
8386 unsigned int ii, jj;
8387 char kick_reason[MAXLEN];
8388
8389 mod_chanmode_init(&change);
8390 change.argc = 1;
8391 change.args[0].mode = MODE_BAN;
8392 for(ii = 0; ii < user->channels.used; ++ii)
8393 {
8394 channel = user->channels.list[ii]->channel;
8395 /* Need not check for bans if they're opped or voiced. */
8396 /* TODO: does this make sense in automode v, h, and o? *
8397 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8398 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE ))
8399 continue;
8400 /* Need not check for bans unless channel registration is active. */
8401 if(!channel->channel_info || IsSuspended(channel->channel_info))
8402 continue;
8403 /* Look for a matching ban already on the channel. */
8404 for(jj = 0; jj < channel->banlist.used; ++jj)
8405 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
8406 break;
8407 /* Need not act if we found one. */
8408 if(jj < channel->banlist.used)
8409 continue;
8410 /* don't kick someone on the userlist */
8411 if(protect_user(user, chanserv, channel->channel_info, true))
8412 continue;
8413 /* Look for a matching ban in this channel. */
8414 for(bData = channel->channel_info->bans; bData; bData = bData->next)
8415 {
8416 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
8417 continue;
8418 change.args[0].u.hostmask = bData->mask;
8419 mod_chanmode_announce(chanserv, channel, &change);
8420 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8421 KickChannelUser(user, channel, chanserv, kick_reason);
8422 bData->triggered = now;
8423 break; /* we don't need to check any more bans in the channel */
8424 }
8425 }
8426 }
8427
8428 static void handle_rename(struct handle_info *handle, const char *old_handle)
8429 {
8430 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
8431
8432 if(dnr)
8433 {
8434 dict_remove2(handle_dnrs, old_handle, 1);
8435 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
8436 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
8437 }
8438 }
8439
8440 static void
8441 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
8442 {
8443 struct userNode *h_user;
8444
8445 if(handle->channels)
8446 {
8447 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
8448 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
8449
8450 while(handle->channels)
8451 del_channel_user(handle->channels, 1);
8452 }
8453 }
8454
8455 static void
8456 handle_server_link(UNUSED_ARG(struct server *server))
8457 {
8458 struct chanData *cData;
8459
8460 for(cData = channelList; cData; cData = cData->next)
8461 {
8462 if(!IsSuspended(cData))
8463 cData->may_opchan = 1;
8464 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
8465 && !cData->channel->join_flooded
8466 && ((cData->channel->limit - cData->channel->members.used)
8467 < chanserv_conf.adjust_threshold))
8468 {
8469 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8470 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8471 }
8472 }
8473 }
8474
8475 static void
8476 chanserv_conf_read(void)
8477 {
8478 dict_t conf_node;
8479 const char *str;
8480 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
8481 struct mod_chanmode *change;
8482 struct string_list *strlist;
8483 struct chanNode *chan;
8484 unsigned int ii;
8485
8486 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
8487 {
8488 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
8489 return;
8490 }
8491 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8492 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8493 chanserv_conf.support_channels.used = 0;
8494 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
8495 {
8496 for(ii = 0; ii < strlist->used; ++ii)
8497 {
8498 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
8499 if(!str2)
8500 str2 = "+nt";
8501 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
8502 LockChannel(chan);
8503 channelList_append(&chanserv_conf.support_channels, chan);
8504 }
8505 }
8506 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
8507 {
8508 const char *str2;
8509 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
8510 if(!str2)
8511 str2 = "+nt";
8512 chan = AddChannel(str, now, str2, NULL, NULL);
8513 LockChannel(chan);
8514 channelList_append(&chanserv_conf.support_channels, chan);
8515 }
8516 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
8517 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
8518 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
8519 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
8520 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
8521 chanserv_conf.greeting_length = str ? atoi(str) : 200;
8522 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
8523 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
8524 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
8525 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
8526 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
8527 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
8528 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
8529 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
8530 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
8531 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
8532 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
8533 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
8534 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
8535 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
8536 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
8537 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
8538 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
8539 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
8540 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
8541 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
8542 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
8543 if(chanserv && str)
8544 NickChange(chanserv, str, 0);
8545 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
8546 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
8547 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
8548 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
8549 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
8550 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
8551 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
8552 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
8553 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
8554 chanserv_conf.max_owned = str ? atoi(str) : 5;
8555 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
8556 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
8557 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
8558 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
8559 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
8560 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
8561 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
8562 god_timeout = str ? ParseInterval(str) : 60*15;
8563 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
8564 if(!str)
8565 str = "+nt";
8566 safestrncpy(mode_line, str, sizeof(mode_line));
8567 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
8568 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
8569 && (change->argc < 2))
8570 {
8571 chanserv_conf.default_modes = *change;
8572 mod_chanmode_free(change);
8573 }
8574 free_string_list(chanserv_conf.set_shows);
8575 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
8576 if(strlist)
8577 strlist = string_list_copy(strlist);
8578 else
8579 {
8580 static const char *list[] = {
8581 /* free form text */
8582 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8583 /* options based on user level */
8584 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8585 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
8586 /* multiple choice options */
8587 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
8588 /* binary options */
8589 "DynLimit", "NoDelete", "BanTimeout",
8590 /* delimiter */
8591 NULL
8592 };
8593 unsigned int ii;
8594 strlist = alloc_string_list(ArrayLength(list)-1);
8595 for(ii=0; list[ii]; ii++)
8596 string_list_append(strlist, strdup(list[ii]));
8597 }
8598 chanserv_conf.set_shows = strlist;
8599 /* We don't look things up now, in case the list refers to options
8600 * defined by modules initialized after this point. Just mark the
8601 * function list as invalid, so it will be initialized.
8602 */
8603 set_shows_list.used = 0;
8604 free_string_list(chanserv_conf.eightball);
8605 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
8606 if(strlist)
8607 {
8608 strlist = string_list_copy(strlist);
8609 }
8610 else
8611 {
8612 strlist = alloc_string_list(4);
8613 string_list_append(strlist, strdup("Yes."));
8614 string_list_append(strlist, strdup("No."));
8615 string_list_append(strlist, strdup("Maybe so."));
8616 }
8617 chanserv_conf.eightball = strlist;
8618 free_string_list(chanserv_conf.old_ban_names);
8619 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
8620 if(strlist)
8621 strlist = string_list_copy(strlist);
8622 else
8623 strlist = alloc_string_list(2);
8624 chanserv_conf.old_ban_names = strlist;
8625 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
8626 off_channel = str ? atoi(str) : 0;
8627 }
8628
8629 static void
8630 chanserv_note_type_read(const char *key, struct record_data *rd)
8631 {
8632 dict_t obj;
8633 struct note_type *ntype;
8634 const char *str;
8635
8636 if(!(obj = GET_RECORD_OBJECT(rd)))
8637 {
8638 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8639 return;
8640 }
8641 if(!(ntype = chanserv_create_note_type(key)))
8642 {
8643 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8644 return;
8645 }
8646
8647 /* Figure out set access */
8648 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8649 {
8650 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8651 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8652 }
8653 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8654 {
8655 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8656 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8657 }
8658 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8659 {
8660 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8661 }
8662 else
8663 {
8664 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8665 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8666 ntype->set_access.min_opserv = 0;
8667 }
8668
8669 /* Figure out visibility */
8670 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8671 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8672 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8673 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8674 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8675 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8676 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8677 ntype->visible_type = NOTE_VIS_ALL;
8678 else
8679 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8680
8681 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8682 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8683 }
8684
8685 static void
8686 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8687 {
8688 struct handle_info *handle;
8689 struct userData *uData;
8690 char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc;
8691 time_t last_seen;
8692 unsigned short access, lastaccess = 0;
8693
8694 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8695 {
8696 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8697 return;
8698 }
8699
8700 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8701 if(access > UL_OWNER)
8702 {
8703 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8704 return;
8705 }
8706
8707 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8708 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8709 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
8710 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8711 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8712 accessexpiry = database_get_data(rd->d.object, KEY_ACCESSEXPIRY, RECDB_QSTRING);
8713 clvlexpiry = database_get_data(rd->d.object, KEY_CLVLEXPIRY, RECDB_QSTRING);
8714 lstacc = database_get_data(rd->d.object, KEY_LASTLEVEL, RECDB_QSTRING);
8715 lastaccess = lstacc ? atoi(lstacc) : 0;
8716
8717 handle = get_handle_info(key);
8718 if(!handle)
8719 {
8720 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8721 return;
8722 }
8723
8724 uData = add_channel_user(chan, handle, access, last_seen, inf, 0);
8725 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8726 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
8727
8728 uData->accessexpiry = accessexpiry ? strtoul(accessexpiry, NULL, 0) : 0;
8729 if (uData->accessexpiry > 0)
8730 timeq_add(uData->accessexpiry, chanserv_expire_tempuser, uData);
8731
8732 uData->clvlexpiry = clvlexpiry ? strtoul(clvlexpiry, NULL, 0) : 0;
8733 if (uData->clvlexpiry > 0)
8734 timeq_add(uData->clvlexpiry, chanserv_expire_tempclvl, uData);
8735
8736 uData->lastaccess = lastaccess;
8737
8738 if((uData->flags & USER_SUSPENDED) && uData->expires)
8739 {
8740 if(uData->expires > now)
8741 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8742 else
8743 uData->flags &= ~USER_SUSPENDED;
8744 }
8745
8746 /* Upgrade: set autoop to the inverse of noautoop */
8747 if(chanserv_read_version < 2)
8748 {
8749 /* if noautoop is true, set autoop false, and vice versa */
8750 if(uData->flags & USER_NOAUTO_OP)
8751 uData->flags = uData->flags & ~USER_AUTO_OP;
8752 else
8753 uData->flags = uData->flags | USER_AUTO_OP;
8754 log_module(CS_LOG, LOG_INFO, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version, uData->flags, key, chan->channel->name);
8755 }
8756
8757 }
8758
8759 static void
8760 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8761 {
8762 struct banData *bData;
8763 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8764 time_t set_time, triggered_time, expires_time;
8765
8766 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8767 {
8768 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8769 return;
8770 }
8771
8772 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8773 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8774 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8775 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8776 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8777 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8778 if (!reason || !owner)
8779 return;
8780
8781 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
8782 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
8783 if(s_expires)
8784 expires_time = (time_t)strtoul(s_expires, NULL, 0);
8785 else if(s_duration)
8786 expires_time = set_time + atoi(s_duration);
8787 else
8788 expires_time = 0;
8789
8790 if(!reason || (expires_time && (expires_time < now)))
8791 return;
8792
8793 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8794 }
8795
8796 static struct suspended *
8797 chanserv_read_suspended(dict_t obj)
8798 {
8799 struct suspended *suspended = calloc(1, sizeof(*suspended));
8800 char *str;
8801 dict_t previous;
8802
8803 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8804 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
8805 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8806 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
8807 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8808 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8809 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8810 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8811 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8812 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8813 return suspended;
8814 }
8815
8816 static struct giveownership *
8817 chanserv_read_giveownership(dict_t obj)
8818 {
8819 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8820 char *str;
8821 dict_t previous;
8822
8823 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8824 giveownership->staff_issuer = str ? strdup(str) : NULL;
8825
8826 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8827
8828 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8829 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8830
8831 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8832 giveownership->reason = str ? strdup(str) : NULL;
8833 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8834 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8835
8836 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8837 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8838 return giveownership;
8839 }
8840
8841 static int
8842 chanserv_channel_read(const char *key, struct record_data *hir)
8843 {
8844 struct suspended *suspended;
8845 struct giveownership *giveownership;
8846 struct mod_chanmode *modes;
8847 struct chanNode *cNode;
8848 struct chanData *cData;
8849 struct dict *channel, *obj;
8850 char *str, *argv[10];
8851 dict_iterator_t it;
8852 unsigned int argc;
8853
8854 channel = hir->d.object;
8855
8856 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8857 if(!str)
8858 str = "<unknown>";
8859 cNode = AddChannel(key, now, NULL, NULL, NULL);
8860 if(!cNode)
8861 {
8862 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8863 return 0;
8864 }
8865 cData = register_channel(cNode, str);
8866 if(!cData)
8867 {
8868 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8869 return 0;
8870 }
8871
8872 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8873 {
8874 enum levelOption lvlOpt;
8875 enum charOption chOpt;
8876
8877 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8878 cData->flags = atoi(str);
8879
8880 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8881 {
8882 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8883 if(str)
8884 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8885 else if(levelOptions[lvlOpt].old_flag)
8886 {
8887 if(cData->flags & levelOptions[lvlOpt].old_flag)
8888 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8889 else
8890 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8891 }
8892 }
8893
8894 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8895 {
8896 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8897 continue;
8898 cData->chOpts[chOpt] = str[0];
8899 }
8900 }
8901 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8902 {
8903 enum levelOption lvlOpt;
8904 enum charOption chOpt;
8905 unsigned int count;
8906
8907 cData->flags = base64toint(str, 5);
8908 count = strlen(str += 5);
8909 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8910 {
8911 unsigned short lvl;
8912 if(levelOptions[lvlOpt].old_flag)
8913 {
8914 if(cData->flags & levelOptions[lvlOpt].old_flag)
8915 lvl = levelOptions[lvlOpt].flag_value;
8916 else
8917 lvl = levelOptions[lvlOpt].default_value;
8918 }
8919 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8920 {
8921 case 'c': lvl = UL_COOWNER; break;
8922 case 'm': lvl = UL_MANAGER; break;
8923 case 'n': lvl = UL_OWNER+1; break;
8924 case 'o': lvl = UL_OP; break;
8925 case 'p': lvl = UL_PEON; break;
8926 case 'h': lvl = UL_HALFOP; break;
8927 case 'w': lvl = UL_OWNER; break;
8928 default: lvl = 0; break;
8929 }
8930 cData->lvlOpts[lvlOpt] = lvl;
8931 }
8932 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8933 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8934 }
8935
8936 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8937 {
8938 suspended = chanserv_read_suspended(obj);
8939 cData->suspended = suspended;
8940 suspended->cData = cData;
8941 /* We could use suspended->expires and suspended->revoked to
8942 * set the CHANNEL_SUSPENDED flag, but we don't. */
8943 }
8944 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8945 {
8946 suspended = calloc(1, sizeof(*suspended));
8947 suspended->issued = 0;
8948 suspended->revoked = 0;
8949 suspended->suspender = strdup(str);
8950 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8951 suspended->expires = str ? atoi(str) : 0;
8952 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8953 suspended->reason = strdup(str ? str : "No reason");
8954 suspended->previous = NULL;
8955 cData->suspended = suspended;
8956 suspended->cData = cData;
8957 }
8958 else
8959 {
8960 cData->flags &= ~CHANNEL_SUSPENDED;
8961 suspended = NULL; /* to squelch a warning */
8962 }
8963
8964 if(IsSuspended(cData)) {
8965 if(suspended->expires > now)
8966 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8967 else if(suspended->expires)
8968 cData->flags &= ~CHANNEL_SUSPENDED;
8969 }
8970
8971 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8972 {
8973 giveownership = chanserv_read_giveownership(obj);
8974 cData->giveownership = giveownership;
8975 }
8976
8977 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8978 struct mod_chanmode change;
8979 mod_chanmode_init(&change);
8980 change.argc = 1;
8981 change.args[0].mode = MODE_CHANOP;
8982 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8983 mod_chanmode_announce(chanserv, cNode, &change);
8984 }
8985
8986 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8987 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
8988 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8989 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
8990 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8991 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
8992 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8993 cData->max = str ? atoi(str) : 0;
8994 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8995 cData->greeting = str ? strdup(str) : NULL;
8996 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8997 cData->user_greeting = str ? strdup(str) : NULL;
8998 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8999 cData->topic_mask = str ? strdup(str) : NULL;
9000 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
9001 cData->topic = str ? strdup(str) : NULL;
9002
9003 if(!IsSuspended(cData)
9004 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
9005 && (argc = split_line(str, 0, ArrayLength(argv), argv))
9006 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
9007 cData->modes = *modes;
9008 if(off_channel > 0)
9009 cData->modes.modes_set |= MODE_REGISTERED;
9010 if(cData->modes.argc > 1)
9011 cData->modes.argc = 1;
9012 mod_chanmode_announce(chanserv, cNode, &cData->modes);
9013 mod_chanmode_free(modes);
9014 }
9015
9016 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
9017 for(it = dict_first(obj); it; it = iter_next(it))
9018 user_read_helper(iter_key(it), iter_data(it), cData);
9019
9020 if(!cData->users && !IsProtected(cData))
9021 {
9022 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
9023 unregister_channel(cData, "has empty user list.");
9024 return 0;
9025 }
9026
9027 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
9028 for(it = dict_first(obj); it; it = iter_next(it))
9029 ban_read_helper(iter_key(it), iter_data(it), cData);
9030
9031 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
9032 for(it = dict_first(obj); it; it = iter_next(it))
9033 {
9034 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
9035 struct record_data *rd = iter_data(it);
9036 const char *note, *setter;
9037
9038 if(rd->type != RECDB_OBJECT)
9039 {
9040 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
9041 }
9042 else if(!ntype)
9043 {
9044 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
9045 }
9046 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
9047 {
9048 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
9049 }
9050 else
9051 {
9052 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
9053 if(!setter) setter = "<unknown>";
9054 chanserv_add_channel_note(cData, ntype, setter, note);
9055 }
9056 }
9057
9058 return 0;
9059 }
9060
9061 static void
9062 chanserv_dnr_read(const char *key, struct record_data *hir)
9063 {
9064 const char *setter, *reason, *str;
9065 struct do_not_register *dnr;
9066 time_t expiry;
9067
9068 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
9069 if(!setter)
9070 {
9071 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
9072 return;
9073 }
9074 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
9075 if(!reason)
9076 {
9077 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
9078 return;
9079 }
9080 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
9081 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
9082 if(expiry && expiry <= now)
9083 return;
9084 dnr = chanserv_add_dnr(key, setter, expiry, reason);
9085 if(!dnr)
9086 return;
9087 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
9088 if(str)
9089 dnr->set = atoi(str);
9090 else
9091 dnr->set = 0;
9092 }
9093
9094 static void
9095 chanserv_version_read(struct dict *section)
9096 {
9097 /* global var.. */
9098 char *str;
9099 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
9100 if(str)
9101 chanserv_read_version = atoi(str);
9102 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
9103 }
9104
9105 static int
9106 chanserv_saxdb_read(struct dict *database)
9107 {
9108 struct dict *section;
9109 dict_iterator_t it;
9110
9111 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
9112 chanserv_version_read(section);
9113
9114 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
9115 for(it = dict_first(section); it; it = iter_next(it))
9116 chanserv_note_type_read(iter_key(it), iter_data(it));
9117
9118 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
9119 for(it = dict_first(section); it; it = iter_next(it))
9120 chanserv_channel_read(iter_key(it), iter_data(it));
9121
9122 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
9123 for(it = dict_first(section); it; it = iter_next(it))
9124 chanserv_dnr_read(iter_key(it), iter_data(it));
9125
9126 return 0;
9127 }
9128
9129 static int
9130 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
9131 {
9132 int high_present = 0;
9133 saxdb_start_record(ctx, KEY_USERS, 1);
9134 for(; uData; uData = uData->next)
9135 {
9136 if((uData->access >= UL_PRESENT) && uData->present)
9137 high_present = 1;
9138 saxdb_start_record(ctx, uData->handle->handle, 0);
9139 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
9140 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
9141 saxdb_write_int(ctx, KEY_ACCESSEXPIRY, uData->accessexpiry);
9142 saxdb_write_int(ctx, KEY_CLVLEXPIRY, uData->clvlexpiry);
9143 saxdb_write_int(ctx, KEY_LASTLEVEL, uData->lastaccess);
9144 if(uData->flags)
9145 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
9146 if(uData->expires)
9147 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
9148 if(uData->info)
9149 saxdb_write_string(ctx, KEY_INFO, uData->info);
9150 saxdb_end_record(ctx);
9151 }
9152 saxdb_end_record(ctx);
9153 return high_present;
9154 }
9155
9156 static void
9157 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
9158 {
9159 if(!bData)
9160 return;
9161 saxdb_start_record(ctx, KEY_BANS, 1);
9162 for(; bData; bData = bData->next)
9163 {
9164 saxdb_start_record(ctx, bData->mask, 0);
9165 saxdb_write_int(ctx, KEY_SET, bData->set);
9166 if(bData->triggered)
9167 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
9168 if(bData->expires)
9169 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
9170 if(bData->owner[0])
9171 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
9172 if(bData->reason)
9173 saxdb_write_string(ctx, KEY_REASON, bData->reason);
9174 saxdb_end_record(ctx);
9175 }
9176 saxdb_end_record(ctx);
9177 }
9178
9179 static void
9180 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
9181 {
9182 saxdb_start_record(ctx, name, 0);
9183 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
9184 saxdb_write_string(ctx, KEY_REASON, susp->reason);
9185 if(susp->issued)
9186 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
9187 if(susp->expires)
9188 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
9189 if(susp->revoked)
9190 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
9191 if(susp->previous)
9192 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
9193 saxdb_end_record(ctx);
9194 }
9195
9196 static void
9197 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
9198 {
9199 saxdb_start_record(ctx, name, 0);
9200 if(giveownership->staff_issuer)
9201 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
9202 if(giveownership->old_owner)
9203 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
9204 if(giveownership->target)
9205 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
9206 if(giveownership->target_access)
9207 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
9208 if(giveownership->reason)
9209 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
9210 if(giveownership->issued)
9211 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
9212 if(giveownership->previous)
9213 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
9214 saxdb_end_record(ctx);
9215 }
9216
9217 static void
9218 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
9219 {
9220 char buf[MAXLEN];
9221 int high_present;
9222 enum levelOption lvlOpt;
9223 enum charOption chOpt;
9224
9225 saxdb_start_record(ctx, channel->channel->name, 1);
9226
9227 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
9228 saxdb_write_int(ctx, KEY_MAX, channel->max);
9229 if(channel->topic)
9230 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
9231 if(channel->registrar)
9232 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
9233 if(channel->greeting)
9234 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
9235 if(channel->user_greeting)
9236 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
9237 if(channel->topic_mask)
9238 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
9239 if(channel->suspended)
9240 chanserv_write_suspended(ctx, "suspended", channel->suspended);
9241 if(channel->giveownership)
9242 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
9243
9244 saxdb_start_record(ctx, KEY_OPTIONS, 0);
9245 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
9246 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9247 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
9248 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9249 {
9250 buf[0] = channel->chOpts[chOpt];
9251 buf[1] = '\0';
9252 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
9253 }
9254 saxdb_end_record(ctx);
9255
9256 if(channel->modes.modes_set || channel->modes.modes_clear)
9257 {
9258 mod_chanmode_format(&channel->modes, buf);
9259 saxdb_write_string(ctx, KEY_MODES, buf);
9260 }
9261
9262 high_present = chanserv_write_users(ctx, channel->users);
9263 chanserv_write_bans(ctx, channel->bans);
9264
9265 if(dict_size(channel->notes))
9266 {
9267 dict_iterator_t it;
9268
9269 saxdb_start_record(ctx, KEY_NOTES, 1);
9270 for(it = dict_first(channel->notes); it; it = iter_next(it))
9271 {
9272 struct note *note = iter_data(it);
9273 saxdb_start_record(ctx, iter_key(it), 0);
9274 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
9275 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
9276 saxdb_end_record(ctx);
9277 }
9278 saxdb_end_record(ctx);
9279 }
9280
9281 if(channel->ownerTransfer)
9282 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
9283 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
9284 saxdb_end_record(ctx);
9285 }
9286
9287 static void
9288 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
9289 {
9290 const char *str;
9291
9292 saxdb_start_record(ctx, ntype->name, 0);
9293 switch(ntype->set_access_type)
9294 {
9295 case NOTE_SET_CHANNEL_ACCESS:
9296 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
9297 break;
9298 case NOTE_SET_CHANNEL_SETTER:
9299 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
9300 break;
9301 case NOTE_SET_PRIVILEGED: default:
9302 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
9303 break;
9304 }
9305 switch(ntype->visible_type)
9306 {
9307 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
9308 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
9309 case NOTE_VIS_PRIVILEGED:
9310 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
9311 }
9312 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
9313 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
9314 saxdb_end_record(ctx);
9315 }
9316
9317 static void
9318 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
9319 {
9320 struct do_not_register *dnr;
9321 dict_iterator_t it, next;
9322
9323 for(it = dict_first(dnrs); it; it = next)
9324 {
9325 next = iter_next(it);
9326 dnr = iter_data(it);
9327 if(dnr->expires && dnr->expires <= now)
9328 {
9329 dict_remove(dnrs, iter_key(it));
9330 continue;
9331 }
9332 saxdb_start_record(ctx, dnr->chan_name, 0);
9333 if(dnr->set)
9334 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
9335 if(dnr->expires)
9336 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
9337 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
9338 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
9339 saxdb_end_record(ctx);
9340 }
9341 }
9342
9343 static int
9344 chanserv_saxdb_write(struct saxdb_context *ctx)
9345 {
9346 dict_iterator_t it;
9347 struct chanData *channel;
9348
9349 /* Version Control*/
9350 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
9351 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
9352 saxdb_end_record(ctx);
9353
9354 /* Notes */
9355 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
9356 for(it = dict_first(note_types); it; it = iter_next(it))
9357 chanserv_write_note_type(ctx, iter_data(it));
9358 saxdb_end_record(ctx);
9359
9360 /* DNRs */
9361 saxdb_start_record(ctx, KEY_DNR, 1);
9362 write_dnrs_helper(ctx, handle_dnrs);
9363 write_dnrs_helper(ctx, plain_dnrs);
9364 write_dnrs_helper(ctx, mask_dnrs);
9365 saxdb_end_record(ctx);
9366
9367 /* Channels */
9368 saxdb_start_record(ctx, KEY_CHANNELS, 1);
9369 for(channel = channelList; channel; channel = channel->next)
9370 chanserv_write_channel(ctx, channel);
9371 saxdb_end_record(ctx);
9372
9373 return 0;
9374 }
9375
9376 static void
9377 chanserv_db_cleanup(void) {
9378 unsigned int ii;
9379 unreg_part_func(handle_part);
9380 while(channelList)
9381 unregister_channel(channelList, "terminating.");
9382 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
9383 UnlockChannel(chanserv_conf.support_channels.list[ii]);
9384 free(chanserv_conf.support_channels.list);
9385 dict_delete(handle_dnrs);
9386 dict_delete(plain_dnrs);
9387 dict_delete(mask_dnrs);
9388 dict_delete(note_types);
9389 free_string_list(chanserv_conf.eightball);
9390 free_string_list(chanserv_conf.old_ban_names);
9391 free_string_list(chanserv_conf.set_shows);
9392 free(set_shows_list.list);
9393 free(uset_shows_list.list);
9394 while(helperList)
9395 {
9396 struct userData *helper = helperList;
9397 helperList = helperList->next;
9398 free(helper);
9399 }
9400 }
9401
9402 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
9403 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9404 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9405
9406 void
9407 init_chanserv(const char *nick)
9408 {
9409 struct chanNode *chan;
9410 unsigned int i;
9411 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
9412 conf_register_reload(chanserv_conf_read);
9413
9414 if(nick)
9415 {
9416 reg_server_link_func(handle_server_link);
9417 reg_new_channel_func(handle_new_channel);
9418 reg_join_func(handle_join);
9419 reg_part_func(handle_part);
9420 reg_kick_func(handle_kick);
9421 reg_topic_func(handle_topic);
9422 reg_mode_change_func(handle_mode);
9423 reg_nick_change_func(handle_nick_change);
9424 reg_auth_func(handle_auth);
9425 }
9426
9427
9428 reg_handle_rename_func(handle_rename);
9429 reg_unreg_func(handle_unreg);
9430
9431 handle_dnrs = dict_new();
9432 dict_set_free_data(handle_dnrs, free);
9433 plain_dnrs = dict_new();
9434 dict_set_free_data(plain_dnrs, free);
9435 mask_dnrs = dict_new();
9436 dict_set_free_data(mask_dnrs, free);
9437
9438 reg_svccmd_unbind_func(handle_svccmd_unbind);
9439 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
9440 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
9441 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
9442 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
9443 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
9444 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
9445 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
9446 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
9447 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
9448 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
9449 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
9450 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
9451 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
9452
9453 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
9454
9455 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
9456 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
9457
9458 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9459 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9460 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9461 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9462 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
9463
9464 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
9465 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
9466 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
9467 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9468 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9469 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9470
9471 DEFINE_COMMAND(levels, 1, 0, NULL);
9472
9473 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9474 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
9475 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9476 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
9477
9478 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
9479 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
9480 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
9481 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
9482 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
9483 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
9484 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
9485 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
9486 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
9487 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
9488
9489 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
9490 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
9491 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
9492 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
9493 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
9494 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
9495 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
9496 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
9497 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
9498 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
9499 DEFINE_CHANNEL_OPTION(unreviewed);
9500 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9501 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9502 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
9503 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
9504 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9505 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9506
9507 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
9508 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
9509 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
9510 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
9511
9512 /* if you change dellamer access, see also places
9513 * like unbanme which have manager hardcoded. */
9514 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
9515 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
9516
9517 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
9518
9519 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
9520
9521 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
9522 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9523 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9524 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9525 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9526 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9527 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9528 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9529 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9530 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9531 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9532 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9533
9534 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
9535 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
9536
9537 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
9538 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
9539 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
9540 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
9541
9542 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
9543 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
9544 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
9545 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
9546 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
9547
9548 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9549 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9550 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9551 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9552 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9553 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9554 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9555 DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9556
9557 /* Channel options */
9558 DEFINE_CHANNEL_OPTION(defaulttopic);
9559 DEFINE_CHANNEL_OPTION(topicmask);
9560 DEFINE_CHANNEL_OPTION(greeting);
9561 DEFINE_CHANNEL_OPTION(usergreeting);
9562 DEFINE_CHANNEL_OPTION(modes);
9563 DEFINE_CHANNEL_OPTION(enfops);
9564 DEFINE_CHANNEL_OPTION(enfhalfops);
9565 DEFINE_CHANNEL_OPTION(automode);
9566 DEFINE_CHANNEL_OPTION(protect);
9567 DEFINE_CHANNEL_OPTION(enfmodes);
9568 DEFINE_CHANNEL_OPTION(enftopic);
9569 DEFINE_CHANNEL_OPTION(pubcmd);
9570 DEFINE_CHANNEL_OPTION(userinfo);
9571 DEFINE_CHANNEL_OPTION(dynlimit);
9572 DEFINE_CHANNEL_OPTION(topicsnarf);
9573 DEFINE_CHANNEL_OPTION(nodelete);
9574 DEFINE_CHANNEL_OPTION(toys);
9575 DEFINE_CHANNEL_OPTION(setters);
9576 DEFINE_CHANNEL_OPTION(topicrefresh);
9577 DEFINE_CHANNEL_OPTION(resync);
9578 DEFINE_CHANNEL_OPTION(ctcpreaction);
9579 DEFINE_CHANNEL_OPTION(bantimeout);
9580 DEFINE_CHANNEL_OPTION(inviteme);
9581 if(off_channel > 1)
9582 DEFINE_CHANNEL_OPTION(offchannel);
9583 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9584
9585 /* Alias set topic to set defaulttopic for compatibility. */
9586 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9587
9588 /* User options */
9589 DEFINE_USER_OPTION(autoinvite);
9590 DEFINE_USER_OPTION(autojoin);
9591 DEFINE_USER_OPTION(info);
9592 DEFINE_USER_OPTION(autoop);
9593
9594 /* Alias uset autovoice to uset autoop. */
9595 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
9596
9597 note_types = dict_new();
9598 dict_set_free_data(note_types, chanserv_deref_note_type);
9599 if(nick)
9600 {
9601 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9602 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9603 service_register(chanserv)->trigger = '!';
9604 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9605 }
9606
9607 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9608
9609 if(chanserv_conf.channel_expire_frequency)
9610 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9611
9612 if(chanserv_conf.dnr_expire_frequency)
9613 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9614
9615 if(chanserv_conf.ban_timeout_frequency)
9616 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
9617
9618 if(chanserv_conf.refresh_period)
9619 {
9620 time_t next_refresh;
9621 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9622 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9623 timeq_add(next_refresh, chanserv_auto_resync, NULL);
9624 }
9625
9626 if (autojoin_channels && chanserv) {
9627 for (i = 0; i < autojoin_channels->used; i++) {
9628 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
9629 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
9630 }
9631 }
9632
9633 reg_exit_func(chanserv_db_cleanup);
9634 message_register_table(msgtab);
9635 }
9636