409056ebde232602760b588aad8c8e8dfbe3f374
[srvx.git] / src / chanserv.c
1 /* chanserv.c - Channel service bot
2  * Copyright 2000-2006 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx 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 2 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 "timeq.h"
28
29 #define CHANSERV_CONF_NAME      "services/chanserv"
30
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL     "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ      "db_backup_freq"
35 #define KEY_INFO_DELAY          "info_delay"
36 #define KEY_MAX_GREETLEN        "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD    "adjust_threshold"
38 #define KEY_ADJUST_DELAY        "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ    "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY   "chan_expire_delay"
41 #define KEY_DNR_EXPIRE_FREQ     "dnr_expire_freq"
42 #define KEY_MAX_CHAN_USERS      "max_chan_users"
43 #define KEY_MAX_CHAN_BANS       "max_chan_bans"
44 #define KEY_NICK                "nick"
45 #define KEY_OLD_CHANSERV_NAME   "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES     "8ball"
47 #define KEY_OLD_BAN_NAMES       "old_ban_names"
48 #define KEY_REFRESH_PERIOD      "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION  "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED               "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET    "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET  "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET  "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL      "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58
59 /* ChanServ database */
60 #define KEY_CHANNELS            "channels"
61 #define KEY_NOTE_TYPES          "note_types"
62
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS  "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS  "setter_access"
67 #define KEY_NOTE_VISIBILITY     "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL        "all"
71 #define KEY_NOTE_MAX_LENGTH     "max_length"
72 #define KEY_NOTE_SETTER         "setter"
73 #define KEY_NOTE_NOTE           "note"
74
75 /* Do-not-register channels */
76 #define KEY_DNR                 "dnr"
77 #define KEY_DNR_SET             "set"
78 #define KEY_DNR_SETTER          "setter"
79 #define KEY_DNR_REASON          "reason"
80
81 /* Channel data */
82 #define KEY_REGISTERED          "registered"
83 #define KEY_REGISTRAR           "registrar"
84 #define KEY_SUSPENDED           "suspended"
85 #define KEY_PREVIOUS            "previous"
86 #define KEY_SUSPENDER           "suspender"
87 #define KEY_ISSUED              "issued"
88 #define KEY_REVOKED             "revoked"
89 #define KEY_SUSPEND_EXPIRES     "suspend_expires"
90 #define KEY_SUSPEND_REASON      "suspend_reason"
91 #define KEY_VISITED             "visited"
92 #define KEY_TOPIC               "topic"
93 #define KEY_GREETING            "greeting"
94 #define KEY_USER_GREETING       "user_greeting"
95 #define KEY_MODES               "modes"
96 #define KEY_FLAGS               "flags"
97 #define KEY_OPTIONS             "options"
98 #define KEY_USERS               "users"
99 #define KEY_BANS                "bans"
100 #define KEY_MAX                 "max"
101 #define KEY_NOTES               "notes"
102 #define KEY_TOPIC_MASK          "topic_mask"
103 #define KEY_OWNER_TRANSFER      "owner_transfer"
104
105 /* User data */
106 #define KEY_LEVEL               "level"
107 #define KEY_INFO                "info"
108 #define KEY_SEEN                "seen"
109
110 /* Ban data */
111 #define KEY_OWNER               "owner"
112 #define KEY_REASON              "reason"
113 #define KEY_SET                 "set"
114 #define KEY_DURATION            "duration"
115 #define KEY_EXPIRES             "expires"
116 #define KEY_TRIGGERED           "triggered"
117
118 #define CHANNEL_DEFAULT_FLAGS   (CHANNEL_OFFCHANNEL)
119 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120
121 /* Administrative messages */
122 static const struct message_entry msgtab[] = {
123     { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124
125 /* Channel registration */
126     { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
127     { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
128     { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
129     { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
130     { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
131     { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132
133 /* Do-not-register channels */
134     { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
135     { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
136     { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
137     { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
138     { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
139     { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
140     { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
141     { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
142     { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
143     { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
144     { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
145     { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
146     { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
147     { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
148
149 /* Channel unregistration */
150     { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
151     { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
152     { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
153     { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
154
155 /* Channel moving */
156     { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
157     { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
158
159 /* Channel merging */
160     { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
161     { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
162     { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
163     { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
164     { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
165
166 /* Handle unregistration */
167     { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
168
169 /* Error messages */
170     { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
171     { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
172     { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
173     { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
174     { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
175     { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
176     { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
177     { "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." },
178     { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
179     { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
180     { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
181     { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
182     { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
183
184 /* Removing yourself from a channel. */
185     { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
186     { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
187     { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
188
189 /* User management */
190     { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
191     { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
192     { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
193     { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
194     { "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." },
195     { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
196     { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
197     { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
198
199     { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
200     { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
201     { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
202     { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
203     { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
204     { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
205     { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
206
207 /* Ban management */
208     { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
209     { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
210     { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
211     { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
212     { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
213     { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
214     { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
215     { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
216     { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
217     { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
218     { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
219     { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
220     { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
221     { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
222     { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
223     { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
224
225     { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
226
227 /* Channel management */
228     { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
229     { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
230     { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
231
232     { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
233     { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
234     { "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" },
235     { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
236     { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
237     { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
238     { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
239
240     { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
241     { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
242     { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
243     { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
244     { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
245     { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
246     { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
247     { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
248     { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
249     { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
250     { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
251     { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
252     { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
253     { "CSMSG_INVALID_NUMERIC",   "$b%d$b is not a valid choice.  Choose one:" },
254     { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
255     { "CSMSG_SET_TOPICMASK",     "$bTopicMask   $b %s" },
256     { "CSMSG_SET_GREETING",      "$bGreeting    $b %s" },
257     { "CSMSG_SET_USERGREETING",  "$bUserGreeting$b %s" },
258     { "CSMSG_SET_MODES",         "$bModes       $b %s" },
259     { "CSMSG_SET_NODELETE",      "$bNoDelete    $b %s" },
260     { "CSMSG_SET_DYNLIMIT",      "$bDynLimit    $b %s" },
261     { "CSMSG_SET_OFFCHANNEL",    "$bOffChannel  $b %s" },
262     { "CSMSG_SET_USERINFO",      "$bUserInfo    $b %d" },
263     { "CSMSG_SET_GIVE_VOICE",    "$bGiveVoice   $b %d" },
264     { "CSMSG_SET_TOPICSNARF",    "$bTopicSnarf  $b %d" },
265     { "CSMSG_SET_INVITEME",      "$bInviteMe    $b %d" },
266     { "CSMSG_SET_ENFOPS",        "$bEnfOps      $b %d" },
267     { "CSMSG_SET_GIVE_OPS",      "$bGiveOps     $b %d" },
268     { "CSMSG_SET_ENFMODES",      "$bEnfModes    $b %d" },
269     { "CSMSG_SET_ENFTOPIC",      "$bEnfTopic    $b %d" },
270     { "CSMSG_SET_PUBCMD",        "$bPubCmd      $b %d" },
271     { "CSMSG_SET_SETTERS",       "$bSetters     $b %d" },
272     { "CSMSG_SET_CTCPUSERS",     "$bCTCPUsers   $b %d" },
273     { "CSMSG_SET_PROTECT",       "$bProtect     $b %d - %s" },
274     { "CSMSG_SET_TOYS",          "$bToys        $b %d - %s" },
275     { "CSMSG_SET_CTCPREACTION",  "$bCTCPReaction$b %d - %s" },
276     { "CSMSG_SET_TOPICREFRESH",  "$bTopicRefresh$b %d - %s" },
277     { "CSMSG_USET_NOAUTOOP",     "$bNoAutoOp    $b %s" },
278     { "CSMSG_USET_NOAUTOVOICE",  "$bNoAutoVoice $b %s" },
279     { "CSMSG_USET_AUTOINVITE",   "$bAutoInvite  $b %s" },
280     { "CSMSG_USET_INFO",         "$bInfo        $b %s" },
281
282     { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
283     { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
284     { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
285     { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
286     { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
287     { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
288     { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
289     { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
290     { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
291     { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
292     { "CSMSG_PROTECT_NONE", "No users will be protected." },
293     { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
294     { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
295     { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
296     { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
297     { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
298     { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
299     { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
300     { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
301     { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
302     { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
303     { "CSMSG_CTCPREACTION_SHORTBAN",  "Short timed ban on disallowed CTCPs" },
304     { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
305
306     { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
307     { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
308     { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
309     { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
310     { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
311     { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
312     { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
313     { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
314
315     { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
316     { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
317     { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
318
319 /* Channel userlist */
320     { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
321     { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
322     { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
323     { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
324
325 /* Channel note list */
326     { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
327     { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
328     { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
329     { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
330     { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
331     { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
332     { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
333     { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
334     { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
335     { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
336     { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
337     { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
338     { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
339     { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
340     { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
341
342 /* Channel [un]suspension */
343     { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
344     { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
345     { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
346     { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
347     { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
348     { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
349     { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
350
351 /* Access information */
352     { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
353     { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
354     { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
355     { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
356     { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
357     { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
358     { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
359     { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
360     { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
361     { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
362     { "CSMSG_OPERATOR_TITLE", "IRC operator" },
363     { "CSMSG_UC_H_TITLE", "network helper" },
364     { "CSMSG_LC_H_TITLE", "support helper" },
365     { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
366
367 /* Seen information */
368     { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
369     { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
370     { "CSMSG_USER_VACATION", "%s is currently on vacation." },
371     { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
372
373 /* Names information */
374     { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
375     { "CSMSG_END_NAMES", "End of names in $b%s$b" },
376
377 /* Channel information */
378     { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
379     { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic:       $b%s" },
380     { "CSMSG_CHANNEL_MODES", "$bMode Lock:           $b%s" },
381     { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
382     { "CSMSG_CHANNEL_MAX", "$bRecord Visitors:     $b%i" },
383     { "CSMSG_CHANNEL_OWNER", "$bOwner:               $b%s" },
384     { "CSMSG_CHANNEL_BANS", "$bBan Count:           $b%i" },
385     { "CSMSG_CHANNEL_USERS", "$bTotal User Count:    $b%i" },
386     { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar:           $b%s" },
387     { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
388     { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
389     { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
390     { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
391     { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
392     { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
393     { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
394     { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
395     { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
396     { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
397     { "CSMSG_CHANNEL_REGISTERED", "$bRegistered:          $b%s ago." },
398     { "CSMSG_CHANNEL_VISITED", "$bVisited:             $b%s ago." },
399
400     { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
401     { "CSMSG_PEEK_TOPIC", "$bTopic:          $b%s" },
402     { "CSMSG_PEEK_MODES", "$bModes:          $b%s" },
403     { "CSMSG_PEEK_USERS", "$bTotal users:    $b%d" },
404     { "CSMSG_PEEK_OPS", "$bOps:$b" },
405     { "CSMSG_PEEK_NO_OPS", "$bOps:            $bNone present" },
406
407 /* Network information */
408     { "CSMSG_NETWORK_INFO", "Network Information:" },
409     { "CSMSG_NETWORK_SERVERS", "$bServers:             $b%i" },
410     { "CSMSG_NETWORK_USERS",   "$bTotal Users:         $b%i" },
411     { "CSMSG_NETWORK_BANS",    "$bTotal Ban Count:     $b%i" },
412     { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count:    $b%i" },
413     { "CSMSG_NETWORK_OPERS",   "$bIRC Operators:       $b%i" },
414     { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
415     { "CSMSG_SERVICES_UPTIME", "$bServices Uptime:     $b%s" },
416     { "CSMSG_BURST_LENGTH",    "$bLast Burst Length:   $b%s" },
417
418 /* Staff list */
419     { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
420     { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
421     { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
422
423 /* Channel searches */
424     { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
425     { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
426     { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
427     { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
428
429 /* Channel configuration */
430     { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
431     { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
432     { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
433     { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
434
435 /* User settings */
436     { "CSMSG_USER_OPTIONS", "User Options:" },
437     { "CSMSG_USER_PROTECTED", "That user is protected." },
438
439 /* Toys */
440     { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
441     { "CSMSG_PING_RESPONSE", "Pong!" },
442     { "CSMSG_WUT_RESPONSE", "wut" },
443     { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number.  Please use a number greater than 1 with this command." },
444     { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b.  Please use either a single number or standard 4d6+3 format." },
445     { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice.  Please use at most %lu." },
446     { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
447     { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
448     { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
449     { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
450
451 /* Other things */
452     { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
453     { NULL, NULL }
454 };
455
456 /* eject_user and unban_user flags */
457 #define ACTION_KICK             0x0001
458 #define ACTION_BAN              0x0002
459 #define ACTION_ADD_BAN          0x0004
460 #define ACTION_ADD_TIMED_BAN    0x0008
461 #define ACTION_UNBAN            0x0010
462 #define ACTION_DEL_BAN          0x0020
463
464 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
465 #define MODELEN                 40 + KEYLEN
466 #define PADLEN                  21
467 #define ACCESSLEN               10
468
469 #define CSFUNC_ARGS             user, channel, argc, argv, cmd
470
471 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
472 #define CHANSERV_SYNTAX()       svccmd_send_help(user, chanserv, cmd)
473 #define REQUIRE_PARAMS(N)       if(argc < (N)) {            \
474         reply("MSG_MISSING_PARAMS", argv[0]); \
475         CHANSERV_SYNTAX(); \
476         return 0; }
477
478 DECLARE_LIST(dnrList, struct do_not_register *);
479 DEFINE_LIST(dnrList, struct do_not_register *);
480
481 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
482
483 struct userNode *chanserv;
484 dict_t note_types;
485 int off_channel;
486 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
487 static struct log_type *CS_LOG;
488
489 static struct
490 {
491     struct channelList  support_channels;
492     struct mod_chanmode default_modes;
493
494     unsigned long       db_backup_frequency;
495     unsigned long       channel_expire_frequency;
496     unsigned long       dnr_expire_frequency;
497
498     long                info_delay;
499     unsigned int        adjust_delay;
500     long                channel_expire_delay;
501     unsigned int        nodelete_level;
502
503     unsigned int        adjust_threshold;
504     int                 join_flood_threshold;
505
506     unsigned int        greeting_length;
507     unsigned int        refresh_period;
508     unsigned int        giveownership_period;
509
510     unsigned int        max_owned;
511     unsigned int        max_chan_users;
512     unsigned int        max_chan_bans;
513     unsigned int        max_userinfo_length;
514
515     struct string_list  *set_shows;
516     struct string_list  *eightball;
517     struct string_list  *old_ban_names;
518
519     const char          *ctcp_short_ban_duration;
520     const char          *ctcp_long_ban_duration;
521
522     const char          *irc_operator_epithet;
523     const char          *network_helper_epithet;
524     const char          *support_helper_epithet;
525 } chanserv_conf;
526
527 struct listData
528 {
529     struct userNode *user;
530     struct userNode *bot;
531     struct chanNode *channel;
532     const char      *search;
533     unsigned short  lowest;
534     unsigned short  highest;
535     struct userData **users;
536     struct helpfile_table table;
537 };
538
539 enum note_access_type
540 {
541     NOTE_SET_CHANNEL_ACCESS,
542     NOTE_SET_CHANNEL_SETTER,
543     NOTE_SET_PRIVILEGED
544 };
545
546 enum note_visible_type
547 {
548     NOTE_VIS_ALL,
549     NOTE_VIS_CHANNEL_USERS,
550     NOTE_VIS_PRIVILEGED
551 };
552
553 struct note_type
554 {
555     enum note_access_type set_access_type;
556     union {
557         unsigned int     min_opserv;
558         unsigned short   min_ulevel;
559     } set_access;
560     enum note_visible_type visible_type;
561     unsigned int         max_length;
562     unsigned int         refs;
563     char                 name[1];
564 };
565
566 struct note
567 {
568     struct note_type     *type;
569     char                 setter[NICKSERV_HANDLE_LEN+1];
570     char                 note[1];
571 };
572
573 static unsigned int registered_channels;
574 static unsigned int banCount;
575
576 static const struct {
577     char *name;
578     char *title;
579     unsigned short level;
580     char ch;
581 } accessLevels[] = {
582     { "peon", "Peon", UL_PEON, '+' },
583     { "op", "Op", UL_OP, '@' },
584     { "master", "Master", UL_MASTER, '%' },
585     { "coowner", "Coowner", UL_COOWNER, '*' },
586     { "owner", "Owner", UL_OWNER, '!' },
587     { "helper", "BUG:", UL_HELPER, 'X' }
588 };
589
590 static const struct {
591     char *format_name;
592     char *db_name;
593     unsigned short default_value;
594     unsigned int old_idx;
595     unsigned int old_flag;
596     unsigned short flag_value;
597 } levelOptions[] = {
598     { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
599     { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
600     { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
601     { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
602     { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
603     { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
604     { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
605     { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
606     { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
607     { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
608     { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
609 };
610
611 struct charOptionValues {
612     char value;
613     char *format_name;
614 } protectValues[] = {
615     { 'a', "CSMSG_PROTECT_ALL" },
616     { 'e', "CSMSG_PROTECT_EQUAL" },
617     { 'l', "CSMSG_PROTECT_LOWER" },
618     { 'n', "CSMSG_PROTECT_NONE" }
619 }, toysValues[] = {
620     { 'd', "CSMSG_TOYS_DISABLED" },
621     { 'n', "CSMSG_TOYS_PRIVATE" },
622     { 'p', "CSMSG_TOYS_PUBLIC" }
623 }, topicRefreshValues[] = {
624     { 'n', "CSMSG_TOPICREFRESH_NEVER" },
625     { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
626     { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
627     { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
628     { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
629 }, ctcpReactionValues[] = {
630     { 'k', "CSMSG_CTCPREACTION_KICK" },
631     { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
632     { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
633     { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
634 };
635
636 static const struct {
637     char *format_name;
638     char *db_name;
639     char default_value;
640     unsigned int old_idx;
641     unsigned char count;
642     struct charOptionValues *values;
643 } charOptions[] = {
644     { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
645     { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
646     { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
647     { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
648 };
649
650 struct userData *helperList;
651 struct chanData *channelList;
652 static struct module *chanserv_module;
653 static unsigned int userCount;
654
655 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
656 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
657 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
658
659 unsigned short
660 user_level_from_name(const char *name, unsigned short clamp_level)
661 {
662     unsigned int level = 0, ii;
663     if(isdigit(name[0]))
664         level = strtoul(name, NULL, 10);
665     else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
666         if(!irccasecmp(name, accessLevels[ii].name))
667             level = accessLevels[ii].level;
668     if(level > clamp_level)
669         return 0;
670     return level;
671 }
672
673 int
674 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
675 {
676     char *sep;
677     *minl = strtoul(arg, &sep, 10);
678     if(*sep == '\0')
679     {
680         *maxl = *minl;
681         return 1;
682     }
683     else if(*sep == '-')
684     {
685         *maxl = strtoul(sep+1, &sep, 10);
686         return *sep == '\0';
687     }
688     else
689         return 0;
690 }
691
692 struct userData*
693 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
694 {
695     struct userData *uData, **head;
696
697     if(!channel || !handle)
698         return NULL;
699
700     if(override && HANDLE_FLAGGED(handle, HELPING)
701        && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
702     {
703         for(uData = helperList;
704             uData && uData->handle != handle;
705             uData = uData->next);
706
707         if(!uData)
708         {
709             uData = calloc(1, sizeof(struct userData));
710             uData->handle = handle;
711
712             uData->access = UL_HELPER;
713             uData->seen = 0;
714
715             uData->info = NULL;
716
717             uData->prev = NULL;
718             uData->next = helperList;
719             if(helperList)
720                 helperList->prev = uData;
721             helperList = uData;
722         }
723
724         head = &helperList;
725     }
726     else
727     {
728         for(uData = channel->users; uData; uData = uData->next)
729             if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
730                 break;
731
732         head = &(channel->users);
733     }
734
735     if(uData && (uData != *head))
736     {
737         /* Shuffle the user to the head of whatever list he was in. */
738         if(uData->next)
739             uData->next->prev = uData->prev;
740         if(uData->prev)
741             uData->prev->next = uData->next;
742
743         uData->prev = NULL;
744         uData->next = *head;
745
746         if(*head)
747             (**head).prev = uData;
748         *head = uData;
749     }
750
751     return uData;
752 }
753
754 /* Returns non-zero if user has at least the minimum access.
755  * exempt_owner is set when handling !set, so the owner can set things
756  * to/from >500.
757  */
758 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
759 {
760     struct userData *uData;
761     struct chanData *cData = channel->channel_info;
762     unsigned short minimum = cData->lvlOpts[opt];
763     if(!minimum)
764         return 1;
765     uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
766     if(!uData)
767         return 0;
768     if(minimum <= uData->access)
769         return 1;
770     if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
771         return 1;
772     return 0;
773 }
774
775 /* Scan for other users authenticated to the same handle
776    still in the channel. If so, keep them listed as present.
777
778    user is optional, if not null, it skips checking that userNode
779    (for the handle_part function) */
780 static void
781 scan_user_presence(struct userData *uData, struct userNode *user)
782 {
783     struct modeNode *mn;
784
785     if(IsSuspended(uData->channel)
786        || IsUserSuspended(uData)
787        || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
788     {
789         uData->present = 0;
790     }
791     else
792     {
793         uData->present = 1;
794         uData->seen = now;
795     }
796 }
797
798 static void
799 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
800 {
801     unsigned int eflags, argc;
802     char *argv[4];
803     static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
804
805     /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
806     if(!channel->channel_info
807        || IsSuspended(channel->channel_info)
808        || IsService(user)
809        || !ircncasecmp(text, "ACTION ", 7))
810         return;
811     /* Figure out the minimum level needed to CTCP the channel */
812     if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
813         return;
814     /* We need to enforce against them; do so. */
815     eflags = 0;
816     argv[0] = (char*)text;
817     argv[1] = user->nick;
818     argc = 2;
819     if(GetUserMode(channel, user))
820         eflags |= ACTION_KICK;
821     switch(channel->channel_info->chOpts[chCTCPReaction]) {
822     default: case 'k': /* just do the kick */ break;
823     case 'b':
824         eflags |= ACTION_BAN;
825         break;
826     case 't':
827         eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
828         argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
829         break;
830     case 'T':
831         eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
832         argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
833         break;
834     }
835     argv[argc++] = bad_ctcp_reason;
836     eject_user(chanserv, channel, argc, argv, NULL, eflags);
837 }
838
839 struct note_type *
840 chanserv_create_note_type(const char *name)
841 {
842     struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
843     strcpy(ntype->name, name);
844     ntype->refs = 1;
845     dict_insert(note_types, ntype->name, ntype);
846     return ntype;
847 }
848
849 static void
850 chanserv_deref_note_type(void *data)
851 {
852     struct note_type *ntype = data;
853
854     if(--ntype->refs > 0)
855         return;
856     free(ntype);
857 }
858
859 static void
860 chanserv_flush_note_type(struct note_type *ntype)
861 {
862     struct chanData *cData;
863     for(cData = channelList; cData; cData = cData->next)
864         dict_remove(cData->notes, ntype->name);
865 }
866
867 static void
868 chanserv_truncate_notes(struct note_type *ntype)
869 {
870     struct chanData *cData;
871     struct note *note;
872     unsigned int size = sizeof(*note) + ntype->max_length;
873     
874     for(cData = channelList; cData; cData = cData->next) {
875         note = dict_find(cData->notes, ntype->name, NULL);
876         if(!note)
877             continue;
878         if(strlen(note->note) <= ntype->max_length)
879             continue;
880         dict_remove2(cData->notes, ntype->name, 1);
881         note = realloc(note, size);
882         note->note[ntype->max_length] = 0;
883         dict_insert(cData->notes, ntype->name, note);
884     }
885 }
886
887 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
888
889 static struct note *
890 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
891 {
892     struct note *note;
893     unsigned int len = strlen(text);
894
895     if(len > type->max_length) len = type->max_length;
896     note = calloc(1, sizeof(*note) + len);
897     note->type = type;
898     strncpy(note->setter, setter, sizeof(note->setter)-1);
899     memcpy(note->note, text, len);
900     note->note[len] = 0;
901     dict_insert(channel->notes, type->name, note);
902     type->refs++;
903     return note;
904 }
905
906 static void
907 chanserv_free_note(void *data)
908 {
909     struct note *note = data;
910
911     chanserv_deref_note_type(note->type);
912     assert(note->type->refs > 0); /* must use delnote to remove the type */
913     free(note);
914 }
915
916 static MODCMD_FUNC(cmd_createnote) {
917     struct note_type *ntype;
918     unsigned int arg = 1, existed = 0, max_length;
919
920     if((ntype = dict_find(note_types, argv[1], NULL)))
921         existed = 1;
922     else
923         ntype = chanserv_create_note_type(argv[arg]);
924     if(!irccasecmp(argv[++arg], "privileged"))
925     {
926         arg++;
927         ntype->set_access_type = NOTE_SET_PRIVILEGED;
928         ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
929     }
930     else if(!irccasecmp(argv[arg], "channel"))
931     {
932         unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
933         if(!ulvl)
934         {
935             reply("CSMSG_INVALID_ACCESS", argv[arg]);
936             goto fail;
937         }
938         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
939         ntype->set_access.min_ulevel = ulvl;
940     }
941     else if(!irccasecmp(argv[arg], "setter"))
942     {
943         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
944     }
945     else
946     {
947         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
948         goto fail;
949     }
950
951     if(!irccasecmp(argv[++arg], "privileged"))
952         ntype->visible_type = NOTE_VIS_PRIVILEGED;
953     else if(!irccasecmp(argv[arg], "channel_users"))
954         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
955     else if(!irccasecmp(argv[arg], "all"))
956         ntype->visible_type = NOTE_VIS_ALL;
957     else {
958         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
959         goto fail;
960     }
961
962     if((arg+1) >= argc) {
963         reply("MSG_MISSING_PARAMS", argv[0]);
964         goto fail;
965     }
966     max_length = strtoul(argv[++arg], NULL, 0);
967     if(max_length < 20 || max_length > 450)
968     {
969         reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
970         goto fail;
971     }
972     if(existed && (max_length < ntype->max_length))
973     {
974         ntype->max_length = max_length;
975         chanserv_truncate_notes(ntype);
976     }
977     ntype->max_length = max_length;
978
979     if(existed)
980         reply("CSMSG_NOTE_MODIFIED", ntype->name);    
981     else
982         reply("CSMSG_NOTE_CREATED", ntype->name);
983     return 1;
984
985 fail:
986     if(!existed)
987         dict_remove(note_types, ntype->name);
988     return 0;
989 }
990
991 static MODCMD_FUNC(cmd_removenote) {
992     struct note_type *ntype;
993     int force;
994
995     ntype = dict_find(note_types, argv[1], NULL);
996     force = (argc > 2) && !irccasecmp(argv[2], "force");
997     if(!ntype)
998     {
999         reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1000         return 0;
1001     }
1002     if(ntype->refs > 1)
1003     {
1004         if(!force)
1005         {
1006             reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1007             return 0;
1008         }
1009         chanserv_flush_note_type(ntype);
1010     }
1011     dict_remove(note_types, argv[1]);
1012     reply("CSMSG_NOTE_DELETED", argv[1]);
1013     return 1;
1014 }
1015
1016 static int
1017 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1018 {
1019     if(!orig)
1020         return 0;
1021     if(orig->modes_set & change->modes_clear)
1022         return 1;
1023     if(orig->modes_clear & change->modes_set)
1024         return 1;
1025     if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1026        && strcmp(orig->new_key, change->new_key))
1027         return 1;
1028     if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1029        && (orig->new_limit != change->new_limit))
1030         return 1;
1031     return 0;
1032 }
1033
1034 static char max_length_text[MAXLEN+1][16];
1035
1036 static struct helpfile_expansion
1037 chanserv_expand_variable(const char *variable)
1038 {
1039     struct helpfile_expansion exp;
1040
1041     if(!irccasecmp(variable, "notes"))
1042     {
1043         dict_iterator_t it;
1044         exp.type = HF_TABLE;
1045         exp.value.table.length = 1;
1046         exp.value.table.width = 3;
1047         exp.value.table.flags = 0;
1048         exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1049         exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1050         exp.value.table.contents[0][0] = "Note Type";
1051         exp.value.table.contents[0][1] = "Visibility";
1052         exp.value.table.contents[0][2] = "Max Length";
1053         for(it=dict_first(note_types); it; it=iter_next(it))
1054         {
1055             struct note_type *ntype = iter_data(it);
1056             int row;
1057
1058             if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1059             row = exp.value.table.length++;
1060             exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1061             exp.value.table.contents[row][0] = ntype->name;
1062             exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1063                 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1064                 "unknown";
1065             if(!max_length_text[ntype->max_length][0])
1066                 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1067             exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1068         }
1069         return exp;
1070     }
1071
1072     exp.type = HF_STRING;
1073     exp.value.str = NULL;
1074     return exp;
1075 }
1076
1077 static struct chanData*
1078 register_channel(struct chanNode *cNode, char *registrar)
1079 {
1080     struct chanData *channel;
1081     enum levelOption lvlOpt;
1082     enum charOption chOpt;
1083
1084     channel = calloc(1, sizeof(struct chanData));
1085
1086     channel->notes = dict_new();
1087     dict_set_free_data(channel->notes, chanserv_free_note);
1088
1089     channel->registrar = strdup(registrar);
1090     channel->registered = now;
1091     channel->visited = now;
1092     channel->limitAdjusted = now;
1093     channel->ownerTransfer = now;
1094     channel->flags = CHANNEL_DEFAULT_FLAGS;
1095     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1096         channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1097     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1098         channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1099
1100     channel->prev = NULL;
1101     channel->next = channelList;
1102
1103     if(channelList)
1104         channelList->prev = channel;
1105     channelList = channel;
1106     registered_channels++;
1107
1108     channel->channel = cNode;
1109     LockChannel(cNode);
1110     cNode->channel_info = channel;
1111
1112     return channel;
1113 }
1114
1115 static struct userData*
1116 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1117 {
1118     struct userData *ud;
1119
1120     if(access > UL_OWNER)
1121         return NULL;
1122
1123     ud = calloc(1, sizeof(*ud));
1124     ud->channel = channel;
1125     ud->handle = handle;
1126     ud->seen = seen;
1127     ud->access = access;
1128     ud->info = info ? strdup(info) : NULL;
1129
1130     ud->prev = NULL;
1131     ud->next = channel->users;
1132     if(channel->users)
1133         channel->users->prev = ud;
1134     channel->users = ud;
1135
1136     channel->userCount++;
1137     userCount++;
1138
1139     ud->u_prev = NULL;
1140     ud->u_next = ud->handle->channels;
1141     if(ud->u_next)
1142         ud->u_next->u_prev = ud;
1143     ud->handle->channels = ud;
1144
1145     return ud;
1146 }
1147
1148 static void unregister_channel(struct chanData *channel, const char *reason);
1149
1150 void
1151 del_channel_user(struct userData *user, int do_gc)
1152 {
1153     struct chanData *channel = user->channel;
1154
1155     channel->userCount--;
1156     userCount--;
1157
1158     if(user->prev)
1159         user->prev->next = user->next;
1160     else
1161         channel->users = user->next;
1162     if(user->next)
1163         user->next->prev = user->prev;
1164
1165     if(user->u_prev)
1166         user->u_prev->u_next = user->u_next;
1167     else
1168         user->handle->channels = user->u_next;
1169     if(user->u_next)
1170         user->u_next->u_prev = user->u_prev;
1171
1172     free(user->info);
1173     free(user);
1174     if(do_gc && !channel->users && !IsProtected(channel))
1175         unregister_channel(channel, "lost all users.");
1176 }
1177
1178 static void expire_ban(void *data);
1179
1180 static struct banData*
1181 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1182 {
1183     struct banData *bd;
1184     unsigned int ii, l1, l2;
1185
1186     if(!mask)
1187         return NULL;
1188
1189     bd = malloc(sizeof(struct banData));
1190
1191     bd->channel = channel;
1192     bd->set = set;
1193     bd->triggered = triggered;
1194     bd->expires = expires;
1195
1196     for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1197     {
1198         extern const char *hidden_host_suffix;
1199         const char *old_name = chanserv_conf.old_ban_names->list[ii];
1200         char *new_mask;
1201
1202         l1 = strlen(mask);
1203         l2 = strlen(old_name);
1204         if(l2+2 > l1)
1205             continue;
1206         if(irccasecmp(mask + l1 - l2, old_name))
1207             continue;
1208         new_mask = alloca(MAXLEN);
1209         sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1210         mask = new_mask;
1211     }
1212     safestrncpy(bd->mask, mask, sizeof(bd->mask));
1213     if(owner)
1214         safestrncpy(bd->owner, owner, sizeof(bd->owner));
1215     bd->reason = strdup(reason);
1216
1217     if(expires)
1218         timeq_add(expires, expire_ban, bd);
1219
1220     bd->prev = NULL;
1221     bd->next = channel->bans;
1222     if(channel->bans)
1223         channel->bans->prev = bd;
1224     channel->bans = bd;
1225     channel->banCount++;
1226     banCount++;
1227
1228     return bd;
1229 }
1230
1231 static void
1232 del_channel_ban(struct banData *ban)
1233 {
1234     ban->channel->banCount--;
1235     banCount--;
1236
1237     if(ban->prev)
1238         ban->prev->next = ban->next;
1239     else
1240         ban->channel->bans = ban->next;
1241
1242     if(ban->next)
1243         ban->next->prev = ban->prev;
1244
1245     if(ban->expires)
1246         timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1247
1248     if(ban->reason)
1249         free(ban->reason);
1250
1251     free(ban);
1252 }
1253
1254 static void
1255 expire_ban(void *data)
1256 {
1257     struct banData *bd = data;
1258     if(!IsSuspended(bd->channel))
1259     {
1260         struct banList bans;
1261         struct mod_chanmode change;
1262         unsigned int ii;
1263         bans = bd->channel->channel->banlist;
1264         mod_chanmode_init(&change);
1265         for(ii=0; ii<bans.used; ii++)
1266         {
1267             if(!strcmp(bans.list[ii]->ban, bd->mask))
1268             {
1269                 change.argc = 1;
1270                 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1271                 change.args[0].u.hostmask = bd->mask;
1272                 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1273                 break;
1274             }
1275         }
1276     }
1277     bd->expires = 0;
1278     del_channel_ban(bd);
1279 }
1280
1281 static void chanserv_expire_suspension(void *data);
1282
1283 static void
1284 unregister_channel(struct chanData *channel, const char *reason)
1285 {
1286     struct mod_chanmode change;
1287     char msgbuf[MAXLEN];
1288
1289     /* After channel unregistration, the following must be cleaned
1290        up:
1291        - Channel information.
1292        - Channel users.
1293        - Channel bans.
1294        - Channel suspension data.
1295        - Timeq entries. (Except timed bans, which are handled elsewhere.)
1296     */
1297
1298     if(!channel)
1299         return;
1300
1301     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1302
1303     if(off_channel > 0)
1304     {
1305       mod_chanmode_init(&change);
1306       change.modes_clear |= MODE_REGISTERED;
1307       mod_chanmode_announce(chanserv, channel->channel, &change);
1308     }
1309
1310     while(channel->users)
1311         del_channel_user(channel->users, 0);
1312
1313     while(channel->bans)
1314         del_channel_ban(channel->bans);
1315
1316     free(channel->topic);
1317     free(channel->registrar);
1318     free(channel->greeting);
1319     free(channel->user_greeting);
1320     free(channel->topic_mask);
1321
1322     if(channel->prev)
1323         channel->prev->next = channel->next;
1324     else
1325         channelList = channel->next;
1326
1327     if(channel->next)
1328         channel->next->prev = channel->prev;
1329
1330     if(channel->suspended)
1331     {
1332         struct chanNode *cNode = channel->channel;
1333         struct suspended *suspended, *next_suspended;
1334
1335         for(suspended = channel->suspended; suspended; suspended = next_suspended)
1336         {
1337             next_suspended = suspended->previous;
1338             free(suspended->suspender);
1339             free(suspended->reason);
1340             if(suspended->expires)
1341                 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1342             free(suspended);
1343         }
1344
1345         if(cNode)
1346             cNode->channel_info = NULL;
1347     }
1348     channel->channel->channel_info = NULL;
1349
1350     dict_delete(channel->notes);
1351     sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1352     if(!IsSuspended(channel))
1353         DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1354     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1355     UnlockChannel(channel->channel);
1356     free(channel);
1357     registered_channels--;
1358 }
1359
1360 static void
1361 expire_channels(UNUSED_ARG(void *data))
1362 {
1363     struct chanData *channel, *next;
1364     struct userData *user;
1365     char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1366
1367     intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1368     sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1369
1370     for(channel = channelList; channel; channel = next)
1371     {
1372         next = channel->next;
1373
1374         /* See if the channel can be expired. */
1375         if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1376            || IsProtected(channel))
1377             continue;
1378
1379         /* Make sure there are no high-ranking users still in the channel. */
1380         for(user=channel->users; user; user=user->next)
1381             if(user->present && (user->access >= UL_PRESENT))
1382                 break;
1383         if(user)
1384             continue;
1385
1386         /* Unregister the channel */
1387         log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1388         unregister_channel(channel, "registration expired.");
1389     }
1390
1391     if(chanserv_conf.channel_expire_frequency)
1392         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1393 }
1394
1395 static void
1396 expire_dnrs(UNUSED_ARG(void *data))
1397 {
1398     dict_iterator_t it;
1399     struct do_not_register *dnr;
1400
1401     for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1402     {
1403         dnr = iter_data(it);
1404         if(!dnr->expires || dnr->expires > now)
1405             continue;
1406         dict_remove(handle_dnrs, dnr->chan_name + 1);
1407     }
1408     for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1409     {
1410         dnr = iter_data(it);
1411         if(!dnr->expires || dnr->expires > now)
1412             continue;
1413         dict_remove(plain_dnrs, dnr->chan_name);
1414     }
1415     for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1416     {
1417         dnr = iter_data(it);
1418         if(!dnr->expires || dnr->expires > now)
1419             continue;
1420         dict_remove(mask_dnrs, dnr->chan_name);
1421     }
1422
1423     if(chanserv_conf.dnr_expire_frequency)
1424         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1425 }
1426
1427 static int
1428 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1429 {
1430     char protect = channel->chOpts[chProtect];
1431     struct userData *cs_victim, *cs_aggressor;
1432
1433     /* Don't protect if no one is to be protected, someone is attacking
1434        himself, or if the aggressor is an IRC Operator. */
1435     if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1436         return 0;
1437
1438     /* Don't protect if the victim isn't authenticated (because they
1439        can't be a channel user), unless we are to protect non-users
1440        also. */
1441     cs_victim = GetChannelAccess(channel, victim->handle_info);
1442     if(protect != 'a' && !cs_victim)
1443         return 0;
1444
1445     /* Protect if the aggressor isn't a user because at this point,
1446        the aggressor can only be less than or equal to the victim. */
1447     cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1448     if(!cs_aggressor)
1449         return 1;
1450
1451     /* If the aggressor was a user, then the victim can't be helped. */
1452     if(!cs_victim)
1453         return 0;
1454
1455     switch(protect)
1456     {
1457     case 'l':
1458         if(cs_victim->access > cs_aggressor->access)
1459             return 1;
1460         break;
1461     case 'a':
1462     case 'e':
1463         if(cs_victim->access >= cs_aggressor->access)
1464             return 1;
1465         break;
1466     }
1467
1468     return 0;
1469 }
1470
1471 static int
1472 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1473 {
1474     struct chanData *cData = channel->channel_info;
1475     struct userData *cs_victim;
1476
1477     if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1478         || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1479        && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1480     {
1481         send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1482         return 0;
1483     }
1484
1485     return 1;
1486 }
1487
1488 static int
1489 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1490 {
1491     if(IsService(victim))
1492     {
1493         send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1494         return 0;
1495     }
1496
1497     if(protect_user(victim, user, channel->channel_info))
1498     {
1499         send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1500         return 0;
1501     }
1502
1503     return 1;
1504 }
1505
1506 static struct do_not_register *
1507 chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
1508 {
1509     struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1510     safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1511     safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1512     strcpy(dnr->reason, reason);
1513     dnr->set = now;
1514     dnr->expires = expires;
1515     if(dnr->chan_name[0] == '*')
1516         dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1517     else if(strpbrk(dnr->chan_name, "*?"))
1518         dict_insert(mask_dnrs, dnr->chan_name, dnr);
1519     else
1520         dict_insert(plain_dnrs, dnr->chan_name, dnr);
1521     return dnr;
1522 }
1523
1524 static struct dnrList
1525 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1526 {
1527     struct dnrList list;
1528     dict_iterator_t it, next;
1529     struct do_not_register *dnr;
1530
1531     dnrList_init(&list);
1532
1533     if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1534     {
1535         if(dnr->expires && dnr->expires <= now)
1536             dict_remove(handle_dnrs, handle);
1537         else if(list.used < max)
1538             dnrList_append(&list, dnr);
1539     }
1540
1541     if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1542     {
1543         if(dnr->expires && dnr->expires <= now)
1544             dict_remove(plain_dnrs, chan_name);
1545         else if(list.used < max)
1546             dnrList_append(&list, dnr);
1547     }
1548
1549     if(chan_name)
1550     {
1551         for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1552         {
1553             next = iter_next(it);
1554             if(!match_ircglob(chan_name, iter_key(it)))
1555                 continue;
1556             dnr = iter_data(it);
1557             if(dnr->expires && dnr->expires <= now)
1558                 dict_remove(mask_dnrs, iter_key(it));
1559             else
1560                 dnrList_append(&list, dnr);
1561         }
1562     }
1563
1564     return list;
1565 }
1566
1567 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1568 {
1569     struct userNode *user;
1570     char buf1[INTERVALLEN];
1571     char buf2[INTERVALLEN];
1572
1573     user = extra;
1574     if(dnr->set)
1575         strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
1576     if(dnr->expires)
1577     {
1578         strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
1579         send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1580     }
1581     else if(dnr->set)
1582     {
1583         send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1584     }
1585     else
1586         send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1587     return 0;
1588 }
1589
1590 static unsigned int
1591 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1592 {
1593     struct dnrList list;
1594     unsigned int ii;
1595
1596     list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1597     for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1598         dnr_print_func(list.list[ii], user);
1599     if(ii < list.used)
1600         reply("CSMSG_MORE_DNRS", list.used - ii);
1601     free(list.list);
1602     return ii;
1603 }
1604
1605 struct do_not_register *
1606 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1607 {
1608     struct dnrList list;
1609     struct do_not_register *dnr;
1610
1611     list = chanserv_find_dnrs(chan_name, handle->handle, 1);
1612     dnr = list.used ? list.list[0] : NULL;
1613     free(list.list);
1614     return dnr;
1615 }
1616
1617 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1618 {
1619     struct do_not_register *dnr;
1620     dict_iterator_t it, next;
1621     unsigned int matches = 0;
1622
1623     for(it = dict_first(dict); it; it = next)
1624     {
1625         dnr = iter_data(it);
1626         next = iter_next(it);
1627         if(dnr->expires && dnr->expires <= now)
1628         {
1629             dict_remove(dict, iter_key(it));
1630             continue;
1631         }
1632         dnr_print_func(dnr, user);
1633         matches++;
1634     }
1635
1636     return matches;
1637 }
1638
1639 static CHANSERV_FUNC(cmd_noregister)
1640 {
1641     const char *target;
1642     time_t expiry, duration;
1643     unsigned int matches;
1644
1645     if(argc < 2)
1646     {
1647         reply("CSMSG_DNR_SEARCH_RESULTS");
1648         matches = send_dnrs(user, handle_dnrs);
1649         matches += send_dnrs(user, plain_dnrs);
1650         matches += send_dnrs(user, mask_dnrs);
1651         if(matches)
1652             reply("MSG_MATCH_COUNT", matches);
1653         else
1654             reply("MSG_NO_MATCHES");
1655         return 0;
1656     }
1657
1658     target = argv[1];
1659
1660     if(!IsChannelName(target) && (*target != '*'))
1661     {
1662         reply("CSMSG_NOT_DNR", target);
1663         return 0;
1664     }
1665
1666     if(argc > 2)
1667     {
1668         if(argc == 3)
1669         {
1670             reply("MSG_INVALID_DURATION", argv[2]);
1671             return 0;
1672         }
1673
1674         if(!strcmp(argv[2], "0"))
1675             expiry = 0;
1676         else if((duration = ParseInterval(argv[2])))
1677             expiry = now + duration;
1678         else
1679         {
1680             reply("MSG_INVALID_DURATION", argv[2]);
1681             return 0;
1682         }
1683
1684         const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1685         if((*target == '*') && !get_handle_info(target + 1))
1686         {
1687             reply("MSG_HANDLE_UNKNOWN", target + 1);
1688             return 0;
1689         }
1690         chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1691         reply("CSMSG_NOREGISTER_CHANNEL", target);
1692         return 1;
1693     }
1694
1695     reply("CSMSG_DNR_SEARCH_RESULTS");
1696     if(*target == '*')
1697         matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1698     else
1699         matches = chanserv_show_dnrs(user, cmd, target, NULL);
1700     if(!matches)
1701         reply("MSG_NO_MATCHES");
1702     return 0;
1703 }
1704
1705 static CHANSERV_FUNC(cmd_allowregister)
1706 {
1707     const char *chan_name = argv[1];
1708
1709     if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1710        || dict_remove(plain_dnrs, chan_name)
1711        || dict_remove(mask_dnrs, chan_name))
1712     {
1713         reply("CSMSG_DNR_REMOVED", chan_name);
1714         return 1;
1715     }
1716     reply("CSMSG_NO_SUCH_DNR", chan_name);
1717     return 0;
1718 }
1719
1720 struct dnr_search {
1721     struct userNode *source;
1722     char *chan_mask;
1723     char *setter_mask;
1724     char *reason_mask;
1725     time_t min_set, max_set;
1726     time_t min_expires, max_expires;
1727     unsigned int limit;
1728 };
1729
1730 static int
1731 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1732 {
1733     return !((dnr->set < search->min_set)
1734              || (dnr->set > search->max_set)
1735              || (dnr->expires && ((dnr->expires < search->min_expires)
1736                                   || (dnr->expires > search->max_expires)))
1737              || (search->chan_mask
1738                  && !match_ircglob(search->chan_mask, dnr->chan_name))
1739              || (search->setter_mask
1740                  && !match_ircglob(search->setter_mask, dnr->setter))
1741              || (search->reason_mask
1742                  && !match_ircglob(search->reason_mask, dnr->reason)));
1743 }
1744
1745 static struct dnr_search *
1746 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1747 {
1748     struct dnr_search *discrim;
1749     unsigned int ii;
1750
1751     discrim = calloc(1, sizeof(*discrim));
1752     discrim->source = user;
1753     discrim->chan_mask = NULL;
1754     discrim->setter_mask = NULL;
1755     discrim->reason_mask = NULL;
1756     discrim->max_set = INT_MAX;
1757     discrim->max_expires = INT_MAX;
1758     discrim->limit = 50;
1759
1760     for(ii=0; ii<argc; ++ii)
1761     {
1762         if(ii == argc - 1)
1763         {
1764             reply("MSG_MISSING_PARAMS", argv[ii]);
1765             goto fail;
1766         }
1767         else if(0 == irccasecmp(argv[ii], "channel"))
1768         {
1769             discrim->chan_mask = argv[++ii];
1770         }
1771         else if(0 == irccasecmp(argv[ii], "setter"))
1772         {
1773             discrim->setter_mask = argv[++ii];
1774         }
1775         else if(0 == irccasecmp(argv[ii], "reason"))
1776         {
1777             discrim->reason_mask = argv[++ii];
1778         }
1779         else if(0 == irccasecmp(argv[ii], "limit"))
1780         {
1781             discrim->limit = strtoul(argv[++ii], NULL, 0);
1782         }
1783         else if(0 == irccasecmp(argv[ii], "set"))
1784         {
1785             const char *cmp = argv[++ii];
1786             if(cmp[0] == '<') {
1787                 if(cmp[1] == '=')
1788                     discrim->min_set = now - ParseInterval(cmp + 2);
1789                 else
1790                     discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1791             } else if(cmp[0] == '=') {
1792                 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1793             } else if(cmp[0] == '>') {
1794                 if(cmp[1] == '=')
1795                     discrim->max_set = now - ParseInterval(cmp + 2);
1796                 else
1797                     discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1798             } else {
1799                 discrim->max_set = now - (ParseInterval(cmp) - 1);
1800             }
1801         }
1802         else if(0 == irccasecmp(argv[ii], "expires"))
1803         {
1804             const char *cmp = argv[++ii];
1805             if(cmp[0] == '<') {
1806                 if(cmp[1] == '=')
1807                     discrim->max_expires = now + ParseInterval(cmp + 2);
1808                 else
1809                     discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1810             } else if(cmp[0] == '=') {
1811                 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1812             } else if(cmp[0] == '>') {
1813                 if(cmp[1] == '=')
1814                     discrim->min_expires = now + ParseInterval(cmp + 2);
1815                 else
1816                     discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1817             } else {
1818                 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1819             }
1820         }
1821         else
1822         {
1823             reply("MSG_INVALID_CRITERIA", argv[ii]);
1824             goto fail;
1825         }
1826     }
1827     return discrim;
1828
1829   fail:
1830     free(discrim);
1831     return NULL;
1832 }
1833
1834 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1835
1836 static unsigned int
1837 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1838 {
1839     struct do_not_register *dnr;
1840     dict_iterator_t next;
1841     dict_iterator_t it;
1842     unsigned int count;
1843     int target_fixed;
1844
1845     /* Initialize local variables. */
1846     count = 0;
1847     target_fixed = 0;
1848     if(discrim->chan_mask)
1849     {
1850         int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1851         if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1852             target_fixed = 1;
1853     }
1854
1855     if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1856     {
1857         /* Check against account-based DNRs. */
1858         dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1859         if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1860             dsf(dnr, data);
1861     }
1862     else if(target_fixed)
1863     {
1864         /* Check against channel-based DNRs. */
1865         dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1866         if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1867             dsf(dnr, data);
1868     }
1869     else
1870     {
1871         /* Exhaustively search account DNRs. */
1872         for(it = dict_first(handle_dnrs); it; it = next)
1873         {
1874             next = iter_next(it);
1875             dnr = iter_data(it);
1876             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1877                 break;
1878         }
1879
1880         /* Do the same for channel DNRs. */
1881         for(it = dict_first(plain_dnrs); it; it = next)
1882         {
1883             next = iter_next(it);
1884             dnr = iter_data(it);
1885             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1886                 break;
1887         }
1888
1889         /* Do the same for wildcarded channel DNRs. */
1890         for(it = dict_first(mask_dnrs); it; it = next)
1891         {
1892             next = iter_next(it);
1893             dnr = iter_data(it);
1894             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1895                 break;
1896         }
1897     }
1898     return count;
1899 }
1900
1901 static int
1902 dnr_remove_func(struct do_not_register *match, void *extra)
1903 {
1904     struct userNode *user;
1905     char *chan_name;
1906
1907     chan_name = alloca(strlen(match->chan_name) + 1);
1908     strcpy(chan_name, match->chan_name);
1909     user = extra;
1910     if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1911        || dict_remove(plain_dnrs, chan_name)
1912        || dict_remove(mask_dnrs, chan_name))
1913     {
1914         send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1915     }
1916     return 0;
1917 }
1918
1919 static MODCMD_FUNC(cmd_dnrsearch)
1920 {
1921     struct dnr_search *discrim;
1922     dnr_search_func action;
1923     struct svccmd *subcmd;
1924     unsigned int matches;
1925     char buf[MAXLEN];
1926
1927     sprintf(buf, "dnrsearch %s", argv[1]);
1928     subcmd = dict_find(cmd->parent->commands, buf, NULL);
1929     if(!subcmd)
1930     {
1931         reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1932         return 0;
1933     }
1934     if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1935         return 0;
1936     if(!irccasecmp(argv[1], "print"))
1937         action = dnr_print_func;
1938     else if(!irccasecmp(argv[1], "remove"))
1939         action = dnr_remove_func;
1940     else
1941     {
1942         reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1943         return 0;
1944     }
1945
1946     discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1947     if(!discrim)
1948         return 0;
1949
1950     if(action == dnr_print_func)
1951         reply("CSMSG_DNR_SEARCH_RESULTS");
1952     matches = dnr_search(discrim, action, user);
1953     if(matches)
1954         reply("MSG_MATCH_COUNT", matches);
1955     else
1956         reply("MSG_NO_MATCHES");
1957     free(discrim);
1958     return 1;
1959 }
1960
1961 unsigned int
1962 chanserv_get_owned_count(struct handle_info *hi)
1963 {
1964     struct userData *cList;
1965     unsigned int owned;
1966
1967     for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1968         if(cList->access == UL_OWNER)
1969             owned++;
1970     return owned;
1971 }
1972
1973 static CHANSERV_FUNC(cmd_register)
1974 {
1975     struct handle_info *handle;
1976     struct chanData *cData;
1977     struct modeNode *mn;
1978     char reason[MAXLEN];
1979     char *chan_name;
1980     unsigned int new_channel, force=0;
1981     struct do_not_register *dnr;
1982
1983     if(channel)
1984     {
1985         if(channel->channel_info)
1986         {
1987             reply("CSMSG_ALREADY_REGGED", channel->name);
1988             return 0;
1989         }
1990
1991         if(channel->bad_channel)
1992         {
1993             reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1994             return 0;
1995         }
1996
1997         if(!IsHelping(user)
1998            && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1999         {
2000             reply("CSMSG_MUST_BE_OPPED", channel->name);
2001             return 0;
2002         }
2003
2004         new_channel = 0;
2005         chan_name = channel->name;
2006     }
2007     else
2008     {
2009         if((argc < 2) || !IsChannelName(argv[1]))
2010         {
2011             reply("MSG_NOT_CHANNEL_NAME");
2012             return 0;
2013         }
2014
2015         if(opserv_bad_channel(argv[1]))
2016         {
2017             reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2018             return 0;
2019         }
2020
2021         new_channel = 1;
2022         chan_name = argv[1];
2023     }
2024
2025     if(argc >= (new_channel+2))
2026     {
2027         if(!IsHelping(user))
2028         {
2029             reply("CSMSG_PROXY_FORBIDDEN");
2030             return 0;
2031         }
2032
2033         if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2034             return 0;
2035         force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2036         dnr = chanserv_is_dnr(chan_name, handle);
2037     }
2038     else
2039     {
2040         handle = user->handle_info;
2041         dnr = chanserv_is_dnr(chan_name, handle);
2042     }
2043     if(dnr && !force)
2044     {
2045         if(!IsHelping(user))
2046             reply("CSMSG_DNR_CHANNEL", chan_name);
2047         else
2048             chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2049         return 0;
2050     }
2051
2052     if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2053     {
2054         reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2055         return 0;
2056     }
2057
2058     if(new_channel)
2059         channel = AddChannel(argv[1], now, NULL, NULL);
2060
2061     cData = register_channel(channel, user->handle_info->handle);
2062     scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2063     cData->modes = chanserv_conf.default_modes;
2064     if(off_channel > 0)
2065         cData->modes.modes_set |= MODE_REGISTERED;
2066     if (IsOffChannel(cData))
2067     {
2068         mod_chanmode_announce(chanserv, channel, &cData->modes);
2069     }
2070     else
2071     {
2072         struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2073         change->args[change->argc].mode = MODE_CHANOP;
2074         change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2075         change->argc++;
2076         mod_chanmode_announce(chanserv, channel, change);
2077         mod_chanmode_free(change);
2078     }
2079
2080     /* Initialize the channel's max user record. */
2081     cData->max = channel->members.used;
2082
2083     if(handle != user->handle_info)
2084         reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2085     else
2086         reply("CSMSG_REG_SUCCESS", channel->name);
2087
2088     sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2089     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2090     return 1;
2091 }
2092
2093 static const char *
2094 make_confirmation_string(struct userData *uData)
2095 {
2096     static char strbuf[16];
2097     char *src;
2098     unsigned int accum;
2099
2100     accum = 0;
2101     for(src = uData->handle->handle; *src; )
2102         accum = accum * 31 + toupper(*src++);
2103     if(uData->channel)
2104         for(src = uData->channel->channel->name; *src; )
2105             accum = accum * 31 + toupper(*src++);
2106     sprintf(strbuf, "%08x", accum);
2107     return strbuf;
2108 }
2109
2110 static CHANSERV_FUNC(cmd_unregister)
2111 {
2112     char *name;
2113     char reason[MAXLEN];
2114     struct chanData *cData;
2115     struct userData *uData;
2116
2117     cData = channel->channel_info;
2118     if(!cData)
2119     {
2120         reply("CSMSG_NOT_REGISTERED", channel->name);
2121         return 0;
2122     }
2123
2124     uData = GetChannelUser(cData, user->handle_info);
2125     if(!uData || (uData->access < UL_OWNER))
2126     {
2127         reply("CSMSG_NO_ACCESS");
2128         return 0;
2129     }
2130
2131     if(IsProtected(cData))
2132     {
2133         reply("CSMSG_UNREG_NODELETE", channel->name);
2134         return 0;
2135     }
2136
2137     if(!IsHelping(user))
2138     {
2139         const char *confirm_string;
2140         if(IsSuspended(cData))
2141         {
2142             reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2143             return 0;
2144         }
2145         confirm_string = make_confirmation_string(uData);
2146         if((argc < 2) || strcmp(argv[1], confirm_string))
2147         {
2148             reply("CSMSG_CONFIRM_UNREG", confirm_string);
2149             return 0;
2150         }
2151     }
2152
2153     sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2154     name = strdup(channel->name);
2155     unregister_channel(cData, reason);
2156     reply("CSMSG_UNREG_SUCCESS", name);
2157     free(name);
2158     return 1;
2159 }
2160
2161 static CHANSERV_FUNC(cmd_move)
2162 {
2163     struct mod_chanmode change;
2164     struct chanNode *target;
2165     struct modeNode *mn;
2166     struct userData *uData;
2167     char reason[MAXLEN];
2168     struct do_not_register *dnr;
2169
2170     REQUIRE_PARAMS(2);
2171
2172     if(IsProtected(channel->channel_info))
2173     {
2174         reply("CSMSG_MOVE_NODELETE", channel->name);
2175         return 0;
2176     }
2177
2178     if(!IsChannelName(argv[1]))
2179     {
2180         reply("MSG_NOT_CHANNEL_NAME");
2181         return 0;
2182     }
2183
2184     if(opserv_bad_channel(argv[1]))
2185     {
2186         reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2187         return 0;
2188     }
2189
2190     if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2191     {
2192         for(uData = channel->channel_info->users; uData; uData = uData->next)
2193         {
2194             if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2195             {
2196                 if(!IsHelping(user))
2197                     reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2198                 else
2199                     chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2200                 return 0;
2201             }
2202         }
2203     }
2204
2205     mod_chanmode_init(&change);
2206     if(!(target = GetChannel(argv[1])))
2207     {
2208         target = AddChannel(argv[1], now, NULL, NULL);
2209         if(!IsSuspended(channel->channel_info))
2210             AddChannelUser(chanserv, target);
2211     }
2212     else if(target->channel_info)
2213     {
2214         reply("CSMSG_ALREADY_REGGED", target->name);
2215         return 0;
2216     }
2217     else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2218             && !IsHelping(user))
2219     {
2220         reply("CSMSG_MUST_BE_OPPED", target->name);
2221         return 0;
2222     }
2223     else if(!IsSuspended(channel->channel_info))
2224     {
2225         change.argc = 1;
2226         change.args[0].mode = MODE_CHANOP;
2227         change.args[0].u.member = AddChannelUser(chanserv, target);
2228         mod_chanmode_announce(chanserv, target, &change);
2229     }
2230
2231     if(off_channel > 0)
2232     {
2233         /* Clear MODE_REGISTERED from old channel, add it to new. */
2234         change.argc = 0;
2235         change.modes_clear = MODE_REGISTERED;
2236         mod_chanmode_announce(chanserv, channel, &change);
2237         change.modes_clear = 0;
2238         change.modes_set = MODE_REGISTERED;
2239         mod_chanmode_announce(chanserv, target, &change);
2240     }
2241
2242     /* Move the channel_info to the target channel; it
2243        shouldn't be necessary to clear timeq callbacks
2244        for the old channel. */
2245     target->channel_info = channel->channel_info;
2246     target->channel_info->channel = target;
2247     channel->channel_info = NULL;
2248
2249     reply("CSMSG_MOVE_SUCCESS", target->name);
2250
2251     sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2252     if(!IsSuspended(target->channel_info))
2253     {
2254         char reason2[MAXLEN];
2255         sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2256         DelChannelUser(chanserv, channel, reason2, 0);
2257     }
2258     UnlockChannel(channel);
2259     LockChannel(target);
2260     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2261     return 1;
2262 }
2263
2264 static void
2265 merge_users(struct chanData *source, struct chanData *target)
2266 {
2267     struct userData *suData, *tuData, *next;
2268     dict_iterator_t it;
2269     dict_t merge;
2270
2271     merge = dict_new();
2272
2273     /* Insert the source's users into the scratch area. */
2274     for(suData = source->users; suData; suData = suData->next)
2275         dict_insert(merge, suData->handle->handle, suData);
2276
2277     /* Iterate through the target's users, looking for
2278        users common to both channels. The lower access is
2279        removed from either the scratch area or target user
2280        list. */
2281     for(tuData = target->users; tuData; tuData = next)
2282     {
2283         struct userData *choice;
2284
2285         next = tuData->next;
2286
2287         /* If a source user exists with the same handle as a target
2288            channel's user, resolve the conflict by removing one. */
2289         suData = dict_find(merge, tuData->handle->handle, NULL);
2290         if(!suData)
2291             continue;
2292
2293         /* Pick the data we want to keep. */
2294         /* If the access is the same, use the later seen time. */
2295         if(suData->access == tuData->access)
2296             choice = (suData->seen > tuData->seen) ? suData : tuData;
2297         else /* Otherwise, keep the higher access level. */
2298             choice = (suData->access > tuData->access) ? suData : tuData;
2299
2300         /* Remove the user that wasn't picked. */
2301         if(choice == tuData)
2302         {
2303             dict_remove(merge, suData->handle->handle);
2304             del_channel_user(suData, 0);
2305         }
2306         else
2307             del_channel_user(tuData, 0);
2308     }
2309
2310     /* Move the remaining users to the target channel. */
2311     for(it = dict_first(merge); it; it = iter_next(it))
2312     {
2313         suData = iter_data(it);
2314
2315         /* Insert the user into the target channel's linked list. */
2316         suData->prev = NULL;
2317         suData->next = target->users;
2318         suData->channel = target;
2319
2320         if(target->users)
2321             target->users->prev = suData;
2322         target->users = suData;
2323
2324         /* Update the user counts for the target channel; the
2325            source counts are left alone. */
2326         target->userCount++;
2327     }
2328
2329     /* Possible to assert (source->users == NULL) here. */
2330     source->users = NULL;
2331     dict_delete(merge);
2332 }
2333
2334 static void
2335 merge_bans(struct chanData *source, struct chanData *target)
2336 {
2337     struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2338
2339     /* Hold on to the original head of the target ban list
2340        to avoid comparing source bans with source bans. */
2341     tFront = target->bans;
2342
2343     /* Perform a totally expensive O(n*m) merge, ick. */
2344     for(sbData = source->bans; sbData; sbData = sNext)
2345     {
2346         /* Flag to track whether the ban's been moved
2347            to the destination yet. */
2348         int moved = 0;
2349
2350         /* Possible to assert (sbData->prev == NULL) here. */
2351         sNext = sbData->next;
2352
2353         for(tbData = tFront; tbData; tbData = tNext)
2354         {
2355             tNext = tbData->next;
2356
2357             /* Perform two comparisons between each source
2358                and target ban, conflicts are resolved by
2359                keeping the broader ban and copying the later
2360                expiration and triggered time. */
2361             if(match_ircglobs(tbData->mask, sbData->mask))
2362             {
2363                 /* There is a broader ban in the target channel that
2364                    overrides one in the source channel; remove the 
2365                    source ban and break. */
2366                 if(sbData->expires > tbData->expires)
2367                     tbData->expires = sbData->expires;
2368                 if(sbData->triggered > tbData->triggered)
2369                     tbData->triggered = sbData->triggered;
2370                 del_channel_ban(sbData);
2371                 break;
2372             }
2373             else if(match_ircglobs(sbData->mask, tbData->mask))
2374             {
2375                 /* There is a broader ban in the source channel that
2376                    overrides one in the target channel; remove the
2377                    target ban, fall through and move the source over. */
2378                 if(tbData->expires > sbData->expires)
2379                     sbData->expires = tbData->expires;
2380                 if(tbData->triggered > sbData->triggered)
2381                     sbData->triggered = tbData->triggered;
2382                 if(tbData == tFront)
2383                     tFront = tNext;
2384                 del_channel_ban(tbData);
2385             }
2386
2387             /* Source bans can override multiple target bans, so
2388                we allow a source to run through this loop multiple
2389                times, but we can only move it once. */
2390             if(moved)
2391                 continue;
2392             moved = 1;
2393
2394             /* Remove the source ban from the source ban list. */
2395             if(sbData->next)
2396                 sbData->next->prev = sbData->prev;
2397
2398             /* Modify the source ban's associated channel. */
2399             sbData->channel = target;
2400
2401             /* Insert the ban into the target channel's linked list. */
2402             sbData->prev = NULL;
2403             sbData->next = target->bans;
2404
2405             if(target->bans)
2406                 target->bans->prev = sbData;
2407             target->bans = sbData;
2408
2409             /* Update the user counts for the target channel. */
2410             target->banCount++;
2411         }
2412     }
2413
2414     /* Possible to assert (source->bans == NULL) here. */
2415     source->bans = NULL;
2416 }
2417
2418 static void
2419 merge_data(struct chanData *source, struct chanData *target)
2420 {
2421     /* Use more recent visited and owner-transfer time; use older
2422      * registered time.  Bitwise or may_opchan.  Use higher max.
2423      * Do not touch last_refresh, ban count or user counts.
2424      */
2425     if(source->visited > target->visited)
2426         target->visited = source->visited;
2427     if(source->registered < target->registered)
2428         target->registered = source->registered;
2429     if(source->ownerTransfer > target->ownerTransfer)
2430         target->ownerTransfer = source->ownerTransfer;
2431     if(source->may_opchan)
2432         target->may_opchan = 1;
2433     if(source->max > target->max)
2434         target->max = source->max;
2435 }
2436
2437 static void
2438 merge_channel(struct chanData *source, struct chanData *target)
2439 {
2440     merge_users(source, target);
2441     merge_bans(source, target);
2442     merge_data(source, target);
2443 }
2444
2445 static CHANSERV_FUNC(cmd_merge)
2446 {
2447     struct userData *target_user;
2448     struct chanNode *target;
2449     char reason[MAXLEN];
2450
2451     REQUIRE_PARAMS(2);
2452
2453     /* Make sure the target channel exists and is registered to the user
2454        performing the command. */
2455     if(!(target = GetChannel(argv[1])))
2456     {
2457         reply("MSG_INVALID_CHANNEL");
2458         return 0;
2459     }
2460
2461     if(!target->channel_info)
2462     {
2463         reply("CSMSG_NOT_REGISTERED", target->name);
2464         return 0;
2465     }
2466
2467     if(IsProtected(channel->channel_info))
2468     {
2469         reply("CSMSG_MERGE_NODELETE");
2470         return 0;
2471     }
2472
2473     if(IsSuspended(target->channel_info))
2474     {
2475         reply("CSMSG_MERGE_SUSPENDED");
2476         return 0;
2477     }
2478
2479     if(channel == target)
2480     {
2481         reply("CSMSG_MERGE_SELF");
2482         return 0;
2483     }
2484
2485     target_user = GetChannelUser(target->channel_info, user->handle_info);
2486     if(!target_user || (target_user->access < UL_OWNER))
2487     {
2488         reply("CSMSG_MERGE_NOT_OWNER");
2489         return 0;
2490     }
2491
2492     /* Merge the channel structures and associated data. */
2493     merge_channel(channel->channel_info, target->channel_info);
2494     sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2495     unregister_channel(channel->channel_info, reason);
2496     reply("CSMSG_MERGE_SUCCESS", target->name);
2497     return 1;
2498 }
2499
2500 static CHANSERV_FUNC(cmd_opchan)
2501 {
2502     struct mod_chanmode change;
2503     if(!IsHelping(user) && !channel->channel_info->may_opchan)
2504     {
2505         reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2506         return 0;
2507     }
2508     channel->channel_info->may_opchan = 0;
2509     mod_chanmode_init(&change);
2510     change.argc = 1;
2511     change.args[0].mode = MODE_CHANOP;
2512     change.args[0].u.member = GetUserMode(channel, chanserv);
2513     mod_chanmode_announce(chanserv, channel, &change);
2514     reply("CSMSG_OPCHAN_DONE", channel->name);
2515     return 1;
2516 }
2517
2518 static CHANSERV_FUNC(cmd_adduser)
2519 {
2520     struct userData *actee;
2521     struct userData *actor, *real_actor;
2522     struct handle_info *handle;
2523     unsigned short access, override = 0;
2524
2525     REQUIRE_PARAMS(3);
2526
2527     if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2528     {
2529         reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2530         return 0;
2531     }
2532
2533     access = user_level_from_name(argv[2], UL_OWNER);
2534     if(!access)
2535     {
2536         reply("CSMSG_INVALID_ACCESS", argv[2]);
2537         return 0;
2538     }
2539
2540     actor = GetChannelUser(channel->channel_info, user->handle_info);
2541     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2542
2543     if(actor->access <= access)
2544     {
2545         reply("CSMSG_NO_BUMP_ACCESS");
2546         return 0;
2547     }
2548
2549     /* Trying to add someone with equal/more access? */
2550     if (!real_actor || real_actor->access <= access)
2551         override = CMD_LOG_OVERRIDE;
2552
2553     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2554         return 0;
2555
2556     if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2557     {
2558         reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2559         return 0;
2560     }
2561
2562     actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2563     scan_user_presence(actee, NULL);
2564     reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2565     return 1 | override;
2566 }
2567
2568 static CHANSERV_FUNC(cmd_clvl)
2569 {
2570     struct handle_info *handle;
2571     struct userData *victim;
2572     struct userData *actor, *real_actor;
2573     unsigned short new_access, override = 0;
2574     int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2575
2576     REQUIRE_PARAMS(3);
2577
2578     actor = GetChannelUser(channel->channel_info, user->handle_info);
2579     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2580
2581     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2582         return 0;
2583
2584     if(handle == user->handle_info && !privileged)
2585     {
2586         reply("CSMSG_NO_SELF_CLVL");
2587         return 0;
2588     }
2589
2590     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2591     {
2592         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2593         return 0;
2594     }
2595
2596     if(actor->access <= victim->access && !privileged)
2597     {
2598         reply("MSG_USER_OUTRANKED", handle->handle);
2599         return 0;
2600     }
2601
2602     new_access = user_level_from_name(argv[2], UL_OWNER);
2603
2604     if(!new_access)
2605     {
2606         reply("CSMSG_INVALID_ACCESS", argv[2]);
2607         return 0;
2608     }
2609
2610     if(new_access >= actor->access && !privileged)
2611     {
2612         reply("CSMSG_NO_BUMP_ACCESS");
2613         return 0;
2614     }
2615
2616     /* Trying to clvl a equal/higher user? */
2617     if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2618         override = CMD_LOG_OVERRIDE;
2619     /* Trying to clvl someone to equal/higher access? */
2620     if(!real_actor || new_access >= real_actor->access)
2621         override = CMD_LOG_OVERRIDE;
2622     /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2623      * If they lower their own access it's not a big problem.
2624      */
2625
2626     victim->access = new_access;
2627     reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2628     return 1 | override;
2629 }
2630
2631 static CHANSERV_FUNC(cmd_deluser)
2632 {
2633     struct handle_info *handle;
2634     struct userData *victim;
2635     struct userData *actor, *real_actor;
2636     unsigned short access, override = 0;
2637     char *chan_name;
2638
2639     REQUIRE_PARAMS(2);
2640
2641     actor = GetChannelUser(channel->channel_info, user->handle_info);
2642     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2643
2644     if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2645         return 0;
2646
2647     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2648     {
2649         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2650         return 0;
2651     }
2652
2653     if(argc > 2)
2654     {
2655         access = user_level_from_name(argv[1], UL_OWNER);
2656         if(!access)
2657         {
2658             reply("CSMSG_INVALID_ACCESS", argv[1]);
2659             return 0;
2660         }
2661         if(access != victim->access)
2662         {
2663             reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2664             return 0;
2665         }
2666     }
2667     else
2668     {
2669         access = victim->access;
2670     }
2671
2672     if((actor->access <= victim->access) && !IsHelping(user))
2673     {
2674         reply("MSG_USER_OUTRANKED", victim->handle->handle);
2675         return 0;
2676     }
2677
2678     /* If people delete themselves it is an override, but they
2679      * could've used deleteme so we don't log it as an override
2680      */
2681     if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2682         override = CMD_LOG_OVERRIDE;
2683
2684     chan_name = strdup(channel->name);
2685     del_channel_user(victim, 1);
2686     reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2687     free(chan_name);
2688     return 1 | override;
2689 }
2690
2691 static int
2692 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2693 {
2694     struct userData *actor, *real_actor, *uData, *next;
2695     unsigned int override = 0;
2696
2697     actor = GetChannelUser(channel->channel_info, user->handle_info);
2698     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2699
2700     if(min_access > max_access)
2701     {
2702         reply("CSMSG_BAD_RANGE", min_access, max_access);
2703         return 0;
2704     }
2705
2706     if((actor->access <= max_access) && !IsHelping(user))
2707     {
2708         reply("CSMSG_NO_ACCESS");
2709         return 0;
2710     }
2711
2712     if(!real_actor || real_actor->access <= max_access)
2713         override = CMD_LOG_OVERRIDE;
2714
2715     for(uData = channel->channel_info->users; uData; uData = next)
2716     {
2717         next = uData->next;
2718
2719         if((uData->access >= min_access)
2720            && (uData->access <= max_access)
2721            && match_ircglob(uData->handle->handle, mask))
2722             del_channel_user(uData, 1);
2723     }
2724
2725     reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2726     return 1 | override;
2727 }
2728
2729 static CHANSERV_FUNC(cmd_mdelowner)
2730 {
2731     return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2732 }
2733
2734 static CHANSERV_FUNC(cmd_mdelcoowner)
2735 {
2736     return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2737 }
2738
2739 static CHANSERV_FUNC(cmd_mdelmaster)
2740 {
2741     return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2742 }
2743
2744 static CHANSERV_FUNC(cmd_mdelop)
2745 {
2746     return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2747 }
2748
2749 static CHANSERV_FUNC(cmd_mdelpeon)
2750 {
2751     return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2752 }
2753
2754 static int
2755 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2756 {
2757     struct banData *bData, *next;
2758     char interval[INTERVALLEN];
2759     unsigned int count;
2760     time_t limit;
2761
2762     count = 0;
2763     limit = now - duration;
2764     for(bData = channel->channel_info->bans; bData; bData = next)
2765     {
2766         next = bData->next;
2767
2768         if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2769             continue;
2770
2771         del_channel_ban(bData);
2772         count++;
2773     }
2774
2775     intervalString(interval, duration, user->handle_info);
2776     send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2777     return 1;
2778 }
2779
2780 static int
2781 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2782 {
2783     struct userData *actor, *uData, *next;
2784     char interval[INTERVALLEN];
2785     unsigned int count;
2786     time_t limit;
2787
2788     actor = GetChannelAccess(channel->channel_info, user->handle_info);
2789     if(min_access > max_access)
2790     {
2791         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2792         return 0;
2793     }
2794
2795     if(!actor || actor->access <= max_access)
2796     {
2797         send_message(user, chanserv, "CSMSG_NO_ACCESS");
2798         return 0;
2799     }
2800
2801     count = 0;
2802     limit = now - duration;
2803     for(uData = channel->channel_info->users; uData; uData = next)
2804     {
2805         next = uData->next;
2806
2807         if((uData->seen > limit)
2808            || uData->present
2809            || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2810             continue;
2811
2812         if(((uData->access >= min_access) && (uData->access <= max_access))
2813            || (!max_access && (uData->access < actor->access)))
2814         {
2815             del_channel_user(uData, 1);
2816             count++;
2817         }
2818     }
2819
2820     if(!max_access)
2821     {
2822         min_access = 1;
2823         max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2824     }
2825     send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2826     return 1;
2827 }
2828
2829 static CHANSERV_FUNC(cmd_trim)
2830 {
2831     unsigned long duration;
2832     unsigned short min_level, max_level;
2833     int vacation;
2834
2835     REQUIRE_PARAMS(3);
2836
2837     vacation = argc > 3 && !strcmp(argv[3], "vacation");
2838     duration = ParseInterval(argv[2]);
2839     if(duration < 60)
2840     {
2841         reply("CSMSG_CANNOT_TRIM");
2842         return 0;
2843     }
2844
2845     if(!irccasecmp(argv[1], "bans"))
2846     {
2847         cmd_trim_bans(user, channel, duration);
2848         return 1;
2849     }
2850     else if(!irccasecmp(argv[1], "users"))
2851     {
2852         cmd_trim_users(user, channel, 0, 0, duration, vacation);
2853         return 1;
2854     }
2855     else if(parse_level_range(&min_level, &max_level, argv[1]))
2856     {
2857         cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2858         return 1;
2859     }
2860     else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2861     {
2862         cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2863         return 1;
2864     }
2865     else
2866     {
2867         reply("CSMSG_INVALID_TRIM", argv[1]);
2868         return 0;
2869     }
2870 }
2871
2872 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2873    to the user. cmd_all takes advantage of this. */
2874 static CHANSERV_FUNC(cmd_up)
2875 {
2876     struct mod_chanmode change;
2877     struct userData *uData;
2878     const char *errmsg;
2879
2880     mod_chanmode_init(&change);
2881     change.argc = 1;
2882     change.args[0].u.member = GetUserMode(channel, user);
2883     if(!change.args[0].u.member)
2884     {
2885         if(argc)
2886             reply("MSG_CHANNEL_ABSENT", channel->name);
2887         return 0;
2888     }
2889
2890     uData = GetChannelAccess(channel->channel_info, user->handle_info);
2891     if(!uData)
2892     {
2893         if(argc)
2894             reply("CSMSG_GODMODE_UP", argv[0]);
2895         return 0;
2896     }
2897     else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2898     {
2899         change.args[0].mode = MODE_CHANOP;
2900         errmsg = "CSMSG_ALREADY_OPPED";
2901     }
2902     else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2903     {
2904         change.args[0].mode = MODE_VOICE;
2905         errmsg = "CSMSG_ALREADY_VOICED";
2906     }
2907     else
2908     {
2909         if(argc)
2910             reply("CSMSG_NO_ACCESS");
2911         return 0;
2912     }
2913     change.args[0].mode &= ~change.args[0].u.member->modes;
2914     if(!change.args[0].mode)
2915     {
2916         if(argc)
2917             reply(errmsg, channel->name);
2918         return 0;
2919     }
2920     modcmd_chanmode_announce(&change);
2921     return 1;
2922 }
2923
2924 static CHANSERV_FUNC(cmd_down)
2925 {
2926     struct mod_chanmode change;
2927
2928     mod_chanmode_init(&change);
2929     change.argc = 1;
2930     change.args[0].u.member = GetUserMode(channel, user);
2931     if(!change.args[0].u.member)
2932     {
2933         if(argc)
2934             reply("MSG_CHANNEL_ABSENT", channel->name);
2935         return 0;
2936     }
2937
2938     if(!change.args[0].u.member->modes)
2939     {
2940         if(argc)
2941             reply("CSMSG_ALREADY_DOWN", channel->name);
2942         return 0;
2943     }
2944
2945     change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2946     modcmd_chanmode_announce(&change);
2947     return 1;
2948 }
2949
2950 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)
2951 {
2952     struct userData *cList;
2953
2954     for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2955     {
2956         if(IsSuspended(cList->channel)
2957            || IsUserSuspended(cList)
2958            || !GetUserMode(cList->channel->channel, user))
2959             continue;
2960
2961         mcmd(user, cList->channel->channel, 0, NULL, cmd);
2962     }
2963
2964     return 1;
2965 }
2966
2967 static CHANSERV_FUNC(cmd_upall)
2968 {
2969     return cmd_all(CSFUNC_ARGS, cmd_up);
2970 }
2971
2972 static CHANSERV_FUNC(cmd_downall)
2973 {
2974     return cmd_all(CSFUNC_ARGS, cmd_down);
2975 }
2976
2977 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2978 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2979
2980 static int
2981 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)
2982 {
2983     unsigned int ii, valid;
2984     struct userNode *victim;
2985     struct mod_chanmode *change;
2986
2987     change = mod_chanmode_alloc(argc - 1);
2988
2989     for(ii=valid=0; ++ii < argc; )
2990     {
2991         if(!(victim = GetUserH(argv[ii])))
2992             continue;
2993         change->args[valid].mode = mode;
2994         change->args[valid].u.member = GetUserMode(channel, victim);
2995         if(!change->args[valid].u.member)
2996             continue;
2997         if(validate && !validate(user, channel, victim))
2998             continue;
2999         valid++;
3000     }
3001
3002     change->argc = valid;
3003     if(valid < (argc-1))
3004         reply("CSMSG_PROCESS_FAILED");
3005     if(valid)
3006     {
3007         modcmd_chanmode_announce(change);
3008         reply(action, channel->name);
3009     }
3010     mod_chanmode_free(change);
3011     return 1;
3012 }
3013
3014 static CHANSERV_FUNC(cmd_op)
3015 {
3016     return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3017 }
3018
3019 static CHANSERV_FUNC(cmd_deop)
3020 {
3021     return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3022 }
3023
3024 static CHANSERV_FUNC(cmd_voice)
3025 {
3026     return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3027 }
3028
3029 static CHANSERV_FUNC(cmd_devoice)
3030 {
3031     return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3032 }
3033
3034 static int
3035 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3036 {
3037     unsigned int ii;
3038
3039     if(victimCount)
3040         *victimCount = 0;
3041     for(ii=0; ii<channel->members.used; ii++)
3042     {
3043         struct modeNode *mn = channel->members.list[ii];
3044
3045         if(IsService(mn->user))
3046             continue;
3047
3048         if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3049             continue;
3050
3051         if(protect_user(mn->user, user, channel->channel_info))
3052             return 1;
3053
3054         if(victims)
3055             victims[(*victimCount)++] = mn;
3056     }
3057     return 0;
3058 }
3059
3060 static int
3061 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3062 {
3063     struct userNode *victim;
3064     struct modeNode **victims;
3065     unsigned int offset, n, victimCount, duration = 0;
3066     char *reason = "Bye.", *ban, *name;
3067     char interval[INTERVALLEN];
3068
3069     offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3070     REQUIRE_PARAMS(offset);
3071     if(argc > offset)
3072     {
3073         reason = unsplit_string(argv + offset, argc - offset, NULL);
3074         if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3075         {
3076             /* Truncate the reason to a length of TOPICLEN, as
3077                the ircd does; however, leave room for an ellipsis
3078                and the kicker's nick. */
3079             sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3080         }
3081     }
3082
3083     if((victim = GetUserH(argv[1])))
3084     {
3085         victims = alloca(sizeof(victims[0]));
3086         victims[0] = GetUserMode(channel, victim);
3087         /* XXX: The comparison with ACTION_KICK is just because all
3088          * other actions can work on users outside the channel, and we
3089          * want to allow those (e.g.  unbans) in that case.  If we add
3090          * some other ejection action for in-channel users, change
3091          * this too. */
3092         victimCount = victims[0] ? 1 : 0;
3093
3094         if(IsService(victim))
3095         {
3096             reply("MSG_SERVICE_IMMUNE", victim->nick);
3097             return 0;
3098         }
3099
3100         if((action == ACTION_KICK) && !victimCount)
3101         {
3102             reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3103             return 0;
3104         }
3105
3106         if(protect_user(victim, user, channel->channel_info))
3107         {
3108             reply("CSMSG_USER_PROTECTED", victim->nick);
3109             return 0;
3110         }
3111
3112         ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3113         name = victim->nick;
3114     }
3115     else
3116     {
3117         if(!is_ircmask(argv[1]))
3118         {
3119             reply("MSG_NICK_UNKNOWN", argv[1]);
3120             return 0;
3121         }
3122
3123         victims = alloca(sizeof(victims[0]) * channel->members.used);
3124
3125         if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3126         {
3127             reply("CSMSG_MASK_PROTECTED", argv[1]);
3128             return 0;
3129         }
3130
3131         if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3132         {
3133             reply("CSMSG_LAME_MASK", argv[1]);
3134             return 0;
3135         }
3136
3137         if((action == ACTION_KICK) && (victimCount == 0))
3138         {
3139             reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3140             return 0;
3141         }
3142
3143         name = ban = strdup(argv[1]);
3144     }
3145
3146     /* Truncate the ban in place if necessary; we must ensure
3147        that 'ban' is a valid ban mask before sanitizing it. */
3148     sanitize_ircmask(ban);
3149
3150     if(action & ACTION_ADD_BAN)
3151     {
3152         struct banData *bData, *next;
3153
3154         if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3155         {
3156             reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3157             free(ban);
3158             return 0;
3159         }
3160
3161         if(action & ACTION_ADD_TIMED_BAN)
3162         {
3163             duration = ParseInterval(argv[2]);
3164
3165             if(duration < 15)
3166             {
3167                 reply("CSMSG_DURATION_TOO_LOW");
3168                 free(ban);
3169                 return 0;
3170             }
3171             else if(duration > (86400 * 365 * 2))
3172             {
3173                 reply("CSMSG_DURATION_TOO_HIGH");
3174                 free(ban);
3175                 return 0;
3176             }
3177         }
3178
3179         for(bData = channel->channel_info->bans; bData; bData = next)
3180         {
3181             if(match_ircglobs(bData->mask, ban))
3182             {
3183                 int exact = !irccasecmp(bData->mask, ban);
3184
3185                 /* The ban is redundant; there is already a ban
3186                    with the same effect in place. */
3187                 if(exact)
3188                 {
3189                     if(bData->reason)
3190                         free(bData->reason);
3191                     bData->reason = strdup(reason);
3192                     safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3193                     if(cmd)
3194                         reply("CSMSG_REASON_CHANGE", ban);
3195                     if(!bData->expires)
3196                         goto post_add_ban;
3197                 }
3198                 if(exact && bData->expires)
3199                 {
3200                     int reset = 0;
3201
3202                     /* If the ban matches an existing one exactly,
3203                        extend the expiration time if the provided
3204                        duration is longer. */
3205                     if(duration && ((time_t)(now + duration) > bData->expires))
3206                     {
3207                         bData->expires = now + duration;
3208                         reset = 1;
3209                     }
3210                     else if(!duration)
3211                     {
3212                         bData->expires = 0;
3213                         reset = 1;
3214                     }
3215
3216                     if(reset)
3217                     {
3218                         /* Delete the expiration timeq entry and
3219                            requeue if necessary. */
3220                         timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3221
3222                         if(bData->expires)
3223                             timeq_add(bData->expires, expire_ban, bData);
3224
3225                         if(!cmd)
3226                         {
3227                             /* automated kickban */
3228                         }
3229                         else if(duration)
3230                             reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3231                         else
3232                             reply("CSMSG_BAN_ADDED", name, channel->name);
3233
3234                         goto post_add_ban;
3235                     }
3236                 }
3237                 if(cmd)
3238                     reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3239
3240                 free(ban);
3241                 return 0;
3242             }
3243
3244             next = bData->next;
3245             if(match_ircglobs(ban, bData->mask))
3246             {
3247                 /* The ban we are adding makes previously existing
3248                    bans redundant; silently remove them. */
3249                 del_channel_ban(bData);
3250             }
3251         }
3252
3253         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);
3254         free(ban);
3255         name = ban = strdup(bData->mask);
3256     }
3257     else if(ban)
3258     {
3259         for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3260         {
3261             extern const char *hidden_host_suffix;
3262             const char *old_name = chanserv_conf.old_ban_names->list[n];
3263             char *new_mask;
3264             unsigned int l1, l2;
3265
3266             l1 = strlen(ban);
3267             l2 = strlen(old_name);
3268             if(l2+2 > l1)
3269                 continue;
3270             if(irccasecmp(ban + l1 - l2, old_name))
3271                 continue;
3272             new_mask = malloc(MAXLEN);
3273             sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3274             free(ban);
3275             name = ban = new_mask;
3276         }
3277     }
3278
3279   post_add_ban:
3280     if(action & ACTION_BAN)
3281     {
3282         unsigned int exists;
3283         struct mod_chanmode *change;
3284
3285         if(channel->banlist.used >= MAXBANS)
3286         {
3287             if(cmd)
3288                 reply("CSMSG_BANLIST_FULL", channel->name);
3289             free(ban);
3290             return 0;
3291         }
3292
3293         exists = ChannelBanExists(channel, ban);
3294         change = mod_chanmode_alloc(victimCount + 1);
3295         for(n = 0; n < victimCount; ++n)
3296         {
3297             change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3298             change->args[n].u.member = victims[n];
3299         }
3300         if(!exists)
3301         {
3302             change->args[n].mode = MODE_BAN;
3303             change->args[n++].u.hostmask = ban;
3304         }
3305         change->argc = n;
3306         if(cmd)
3307             modcmd_chanmode_announce(change);
3308         else
3309             mod_chanmode_announce(chanserv, channel, change);
3310         mod_chanmode_free(change);
3311
3312         if(exists && (action == ACTION_BAN))
3313         {
3314             if(cmd)
3315                 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3316             free(ban);
3317             return 0;
3318         }
3319     }
3320
3321     if(action & ACTION_KICK)
3322     {
3323         char kick_reason[MAXLEN];
3324         sprintf(kick_reason, "(%s) %s", user->nick, reason);
3325
3326         for(n = 0; n < victimCount; n++)
3327             KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3328     }
3329
3330     if(!cmd)
3331     {
3332         /* No response, since it was automated. */
3333     }
3334     else if(action & ACTION_ADD_BAN)
3335     {
3336         if(duration)
3337             reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3338         else
3339             reply("CSMSG_BAN_ADDED", name, channel->name);
3340     }
3341     else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3342         reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3343     else if(action & ACTION_BAN)
3344         reply("CSMSG_BAN_DONE", name, channel->name);
3345     else if(action & ACTION_KICK && victimCount)
3346         reply("CSMSG_KICK_DONE", name, channel->name);
3347
3348     free(ban);
3349     return 1;
3350 }
3351
3352 static CHANSERV_FUNC(cmd_kickban)
3353 {
3354     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3355 }
3356
3357 static CHANSERV_FUNC(cmd_kick)
3358 {
3359     return eject_user(CSFUNC_ARGS, ACTION_KICK);
3360 }
3361
3362 static CHANSERV_FUNC(cmd_ban)
3363 {
3364     return eject_user(CSFUNC_ARGS, ACTION_BAN);
3365 }
3366
3367 static CHANSERV_FUNC(cmd_addban)
3368 {
3369     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3370 }
3371
3372 static CHANSERV_FUNC(cmd_addtimedban)
3373 {
3374     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3375 }
3376
3377 static struct mod_chanmode *
3378 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3379 {
3380     struct mod_chanmode *change;
3381     unsigned char *match;
3382     unsigned int ii, count;
3383
3384     match = alloca(bans->used);
3385     if(actee)
3386     {
3387         for(ii = count = 0; ii < bans->used; ++ii)
3388         {
3389             match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3390                                           MATCH_USENICK | MATCH_VISIBLE);
3391             if(match[ii])
3392                 count++;
3393         }
3394     }
3395     else
3396     {
3397         for(ii = count = 0; ii < bans->used; ++ii)
3398         {
3399             match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3400             if(match[ii])
3401                 count++;
3402         }
3403     }
3404     if(!count)
3405         return NULL;
3406     change = mod_chanmode_alloc(count);
3407     for(ii = count = 0; ii < bans->used; ++ii)
3408     {
3409         if(!match[ii])
3410             continue;
3411         change->args[count].mode = MODE_REMOVE | MODE_BAN;
3412         change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3413     }
3414     assert(count == change->argc);
3415     return change;
3416 }
3417
3418 static int
3419 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3420 {
3421     struct userNode *actee;
3422     char *mask = NULL;
3423     int acted = 0;
3424
3425     REQUIRE_PARAMS(2);
3426
3427     /* may want to allow a comma delimited list of users... */
3428     if(!(actee = GetUserH(argv[1])))
3429     {
3430         if(!is_ircmask(argv[1]))
3431         {
3432             reply("MSG_NICK_UNKNOWN", argv[1]);
3433             return 0;
3434         }
3435
3436         mask = strdup(argv[1]);
3437     }
3438
3439     /* We don't sanitize the mask here because ircu
3440        doesn't do it. */
3441     if(action & ACTION_UNBAN)
3442     {
3443         struct mod_chanmode *change;
3444         change = find_matching_bans(&channel->banlist, actee, mask);
3445         if(change)
3446         {
3447             unsigned int ii;
3448
3449             modcmd_chanmode_announce(change);
3450             for(ii = 0; ii < change->argc; ++ii)
3451                 free((char*)change->args[ii].u.hostmask);
3452             mod_chanmode_free(change);
3453             acted = 1;
3454         }
3455     }
3456
3457     if(action & ACTION_DEL_BAN)
3458     {
3459         struct banData *ban, *next;
3460
3461         ban = channel->channel_info->bans;
3462         while(ban)
3463         {
3464             if(actee)
3465                 for( ; ban && !user_matches_glob(actee, ban->mask,
3466                                                  MATCH_USENICK | MATCH_VISIBLE);
3467                      ban = ban->next);
3468             else
3469                 for( ; ban && !match_ircglobs(mask, ban->mask);
3470                      ban = ban->next);
3471             if(!ban)
3472                 break;
3473             next = ban->next;
3474             del_channel_ban(ban);
3475             ban = next;
3476             acted = 1;
3477         }
3478     }
3479
3480     if(!acted)
3481         reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3482     else
3483         reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3484     if(mask)
3485         free(mask);
3486     return 1;
3487 }
3488
3489 static CHANSERV_FUNC(cmd_unban)
3490 {
3491     return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3492 }
3493
3494 static CHANSERV_FUNC(cmd_delban)
3495 {
3496     /* it doesn't necessarily have to remove the channel ban - may want
3497        to make that an option. */
3498     return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3499 }
3500
3501 static CHANSERV_FUNC(cmd_unbanme)
3502 {
3503     struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3504     long flags = ACTION_UNBAN;
3505
3506     /* remove permanent bans if the user has the proper access. */
3507     if(uData->access >= UL_MASTER)
3508         flags |= ACTION_DEL_BAN;
3509
3510     argv[1] = user->nick;
3511     return unban_user(user, channel, 2, argv, cmd, flags);
3512 }
3513
3514 static CHANSERV_FUNC(cmd_unbanall)
3515 {
3516     struct mod_chanmode *change;
3517     unsigned int ii;
3518
3519     if(!channel->banlist.used)
3520     {
3521         reply("CSMSG_NO_BANS", channel->name);
3522         return 0;
3523     }
3524
3525     change = mod_chanmode_alloc(channel->banlist.used);
3526     for(ii=0; ii<channel->banlist.used; ii++)
3527     {
3528         change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3529         change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3530     }
3531     modcmd_chanmode_announce(change);
3532     for(ii = 0; ii < change->argc; ++ii)
3533         free((char*)change->args[ii].u.hostmask);
3534     mod_chanmode_free(change);
3535     reply("CSMSG_BANS_REMOVED", channel->name);
3536     return 1;
3537 }
3538
3539 static CHANSERV_FUNC(cmd_open)
3540 {
3541     struct mod_chanmode *change;
3542     unsigned int ii;
3543
3544     change = find_matching_bans(&channel->banlist, user, NULL);
3545     if(!change)
3546         change = mod_chanmode_alloc(0);
3547     change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3548     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3549        && channel->channel_info->modes.modes_set)
3550         change->modes_clear &= ~channel->channel_info->modes.modes_set;
3551     modcmd_chanmode_announce(change);
3552     reply("CSMSG_CHANNEL_OPENED", channel->name);
3553     for(ii = 0; ii < change->argc; ++ii)
3554         free((char*)change->args[ii].u.hostmask);
3555     mod_chanmode_free(change);
3556     return 1;
3557 }
3558
3559 static CHANSERV_FUNC(cmd_myaccess)
3560 {
3561     static struct string_buffer sbuf;
3562     struct handle_info *target_handle;
3563     struct userData *uData;
3564
3565     if(argc < 2)
3566         target_handle = user->handle_info;
3567     else if(!IsHelping(user))
3568     {
3569         reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3570         return 0;
3571     }
3572     else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3573         return 0;
3574
3575     if(!target_handle->channels)
3576     {
3577         reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3578         return 1;
3579     }
3580
3581     reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3582     for(uData = target_handle->channels; uData; uData = uData->u_next)
3583     {
3584         struct chanData *cData = uData->channel;
3585
3586         if(uData->access > UL_OWNER)
3587             continue;
3588         if(IsProtected(cData)
3589            && (target_handle != user->handle_info)
3590            && !GetTrueChannelAccess(cData, user->handle_info))
3591             continue;
3592         sbuf.used = 0;
3593         string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3594         if(uData->flags != USER_AUTO_OP)
3595             string_buffer_append(&sbuf, ',');
3596         if(IsUserSuspended(uData))
3597             string_buffer_append(&sbuf, 's');
3598         if(IsUserAutoOp(uData))
3599         {
3600             if(uData->access >= cData->lvlOpts[lvlGiveOps])
3601                 string_buffer_append(&sbuf, 'o');
3602             else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3603                 string_buffer_append(&sbuf, 'v');
3604         }
3605         if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3606             string_buffer_append(&sbuf, 'i');
3607         if(uData->info)
3608             string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3609         else
3610             string_buffer_append_string(&sbuf, ")]");
3611         string_buffer_append(&sbuf, '\0');
3612         send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3613     }
3614
3615     return 1;
3616 }
3617
3618 static CHANSERV_FUNC(cmd_access)
3619 {
3620     struct userNode *target;
3621     struct handle_info *target_handle;
3622     struct userData *uData;
3623     int helping;
3624     char prefix[MAXLEN];
3625
3626     if(argc < 2)
3627     {
3628         target = user;
3629         target_handle = target->handle_info;
3630     }
3631     else if((target = GetUserH(argv[1])))
3632     {
3633         target_handle = target->handle_info;
3634     }
3635     else if(argv[1][0] == '*')
3636     {
3637         if(!(target_handle = get_handle_info(argv[1]+1)))
3638         {
3639             reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3640             return 0;
3641         }
3642     }
3643     else
3644     {
3645         reply("MSG_NICK_UNKNOWN", argv[1]);
3646         return 0;
3647     }
3648
3649     assert(target || target_handle);
3650
3651     if(target == chanserv)
3652     {
3653         reply("CSMSG_IS_CHANSERV");
3654         return 1;
3655     }
3656
3657     if(!target_handle)
3658     {
3659         if(IsOper(target))
3660         {
3661             reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3662             return 0;
3663         }
3664         if(target != user)
3665         {
3666             reply("MSG_USER_AUTHENTICATE", target->nick);
3667             return 0;
3668         }
3669         reply("MSG_AUTHENTICATE");
3670         return 0;
3671     }
3672
3673     if(target)
3674     {
3675         const char *epithet = NULL, *type = NULL;
3676         if(IsOper(target))
3677         {
3678             epithet = chanserv_conf.irc_operator_epithet;
3679             type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3680         }
3681         else if(IsNetworkHelper(target))
3682         {
3683             epithet = chanserv_conf.network_helper_epithet;
3684             type = user_find_message(user, "CSMSG_UC_H_TITLE");
3685         }
3686         else if(IsSupportHelper(target))
3687         {
3688             epithet = chanserv_conf.support_helper_epithet;
3689             type = user_find_message(user, "CSMSG_LC_H_TITLE");
3690         }
3691         if(epithet)
3692         {
3693             if(target_handle->epithet)
3694                 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3695             else if(epithet)
3696                 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3697         }
3698         sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3699     }
3700     else
3701     {
3702         sprintf(prefix, "%s", target_handle->handle);
3703     }
3704
3705     if(!channel->channel_info)
3706     {
3707         reply("CSMSG_NOT_REGISTERED", channel->name);
3708         return 1;
3709     }
3710
3711     helping = HANDLE_FLAGGED(target_handle, HELPING)
3712         && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3713     if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3714     {
3715         reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3716         /* To prevent possible information leaks, only show infolines
3717          * if the requestor is in the channel or it's their own
3718          * handle. */
3719         if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3720         {
3721             send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3722         }
3723         /* Likewise, only say it's suspended if the user has active
3724          * access in that channel or it's their own entry. */
3725         if(IsUserSuspended(uData)
3726            && (GetChannelUser(channel->channel_info, user->handle_info)
3727                || (user->handle_info == uData->handle)))
3728         {
3729             reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3730         }
3731     }
3732     else
3733     {
3734         reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3735     }
3736
3737     return 1;
3738 }
3739
3740 static void
3741 zoot_list(struct listData *list)
3742 {
3743     struct userData *uData;
3744     unsigned int start, curr, highest, lowest;
3745     struct helpfile_table tmp_table;
3746     const char **temp, *msg;
3747
3748     if(list->table.length == 1)
3749     {
3750         if(list->search)
3751             send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3752         else
3753             send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3754         msg = user_find_message(list->user, "MSG_NONE");
3755         send_message_type(4, list->user, list->bot, "  %s", msg);
3756     }
3757     tmp_table.width = list->table.width;
3758     tmp_table.flags = list->table.flags;
3759     list->table.contents[0][0] = " ";
3760     highest = list->highest;
3761     if(list->lowest != 0)
3762         lowest = list->lowest;
3763     else if(highest < 100)
3764         lowest = 1;
3765     else
3766         lowest = highest - 100;
3767     for(start = curr = 1; curr < list->table.length; )
3768     {
3769         uData = list->users[curr-1];
3770         list->table.contents[curr++][0] = " ";
3771         if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3772         {
3773             if(list->search)
3774                 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3775             else
3776                 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3777             temp = list->table.contents[--start];
3778             list->table.contents[start] = list->table.contents[0];
3779             tmp_table.contents = list->table.contents + start;
3780             tmp_table.length = curr - start;
3781             table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3782             list->table.contents[start] = temp;
3783             start = curr;
3784             highest = lowest - 1;
3785             lowest = (highest < 100) ? 0 : (highest - 99);
3786         }
3787     }
3788 }
3789
3790 static void
3791 def_list(struct listData *list)
3792 {
3793     const char *msg;
3794     if(list->search)
3795         send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3796     else
3797         send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3798     table_send(list->bot, list->user->nick, 0, NULL, list->table);
3799     if(list->table.length == 1)
3800     {
3801         msg = user_find_message(list->user, "MSG_NONE");
3802         send_message_type(4, list->user, list->bot, "  %s", msg);
3803     }
3804 }
3805
3806 static int
3807 userData_access_comp(const void *arg_a, const void *arg_b)
3808 {
3809     const struct userData *a = *(struct userData**)arg_a;
3810     const struct userData *b = *(struct userData**)arg_b;
3811     int res;
3812     if(a->access != b->access)
3813         res = b->access - a->access;
3814     else
3815         res = irccasecmp(a->handle->handle, b->handle->handle);
3816     return res;
3817 }
3818
3819 static int
3820 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3821 {
3822     void (*send_list)(struct listData *);
3823     struct userData *uData;
3824     struct listData lData;
3825     unsigned int matches;
3826     const char **ary;
3827
3828     lData.user = user;
3829     lData.bot = cmd->parent->bot;
3830     lData.channel = channel;
3831     lData.lowest = lowest;
3832     lData.highest = highest;
3833     lData.search = (argc > 1) ? argv[1] : NULL;
3834     send_list = def_list;
3835     (void)zoot_list; /* since it doesn't show user levels */
3836
3837     if(user->handle_info)
3838     {
3839         switch(user->handle_info->userlist_style)
3840         {
3841         case HI_STYLE_DEF: send_list = def_list; break;
3842         case HI_STYLE_ZOOT: send_list = def_list; break;
3843         }
3844     }
3845
3846     lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3847     matches = 0;
3848     for(uData = channel->channel_info->users; uData; uData = uData->next)
3849     {
3850         if((uData->access < lowest)
3851            || (uData->access > highest)
3852            || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3853             continue;
3854         lData.users[matches++] = uData;
3855     }
3856     qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3857
3858     lData.table.length = matches+1;
3859     lData.table.width = 4;
3860     lData.table.flags = TABLE_NO_FREE;
3861     lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3862     ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3863     lData.table.contents[0] = ary;
3864     ary[0] = "Access";
3865     ary[1] = "Account";
3866     ary[2] = "Last Seen";
3867     ary[3] = "Status";
3868     for(matches = 1; matches < lData.table.length; ++matches)
3869     {
3870         struct userData *uData = lData.users[matches-1];
3871         char seen[INTERVALLEN];
3872
3873         ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3874         lData.table.contents[matches] = ary;
3875         ary[0] = strtab(uData->access);
3876         ary[1] = uData->handle->handle;
3877         if(uData->present)
3878             ary[2] = "Here";
3879         else if(!uData->seen)
3880             ary[2] = "Never";
3881         else
3882             ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3883         ary[2] = strdup(ary[2]);
3884         if(IsUserSuspended(uData))
3885             ary[3] = "Suspended";
3886         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3887             ary[3] = "Vacation";
3888         else
3889             ary[3] = "Normal";
3890     }
3891     send_list(&lData);
3892     for(matches = 1; matches < lData.table.length; ++matches)
3893     {
3894         free((char*)lData.table.contents[matches][2]);
3895         free(lData.table.contents[matches]);
3896     }
3897     free(lData.table.contents[0]);
3898     free(lData.table.contents);
3899     return 1;
3900 }
3901
3902 static CHANSERV_FUNC(cmd_users)
3903 {
3904     return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3905 }
3906
3907 static CHANSERV_FUNC(cmd_wlist)
3908 {
3909     return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3910 }
3911
3912 static CHANSERV_FUNC(cmd_clist)
3913 {
3914     return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3915 }
3916
3917 static CHANSERV_FUNC(cmd_mlist)
3918 {
3919     return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3920 }
3921
3922 static CHANSERV_FUNC(cmd_olist)
3923 {
3924     return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3925 }
3926
3927 static CHANSERV_FUNC(cmd_plist)
3928 {
3929     return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3930 }
3931
3932 static CHANSERV_FUNC(cmd_bans)
3933 {
3934     struct userNode *search_u = NULL;
3935     struct helpfile_table tbl;
3936     unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3937     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3938     const char *msg_never, *triggered, *expires;
3939     struct banData *ban, **bans;
3940
3941     if(argc < 2)
3942         search = NULL;
3943     else if(strchr(search = argv[1], '!'))
3944     {
3945         search = argv[1];
3946         search_wilds = search[strcspn(search, "?*")];
3947     }
3948     else if(!(search_u = GetUserH(search)))
3949         reply("MSG_NICK_UNKNOWN", search);
3950
3951     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3952
3953     for(ban = channel->channel_info->bans; ban; ban = ban->next)
3954     {
3955         if(search_u)
3956         {
3957             if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3958                 continue;
3959         }
3960         else if(search)
3961         {
3962             if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3963                 continue;
3964         }
3965         bans[matches++] = ban;
3966         if(ban->expires)
3967             timed = 1;
3968     }
3969
3970     tbl.length = matches + 1;
3971     tbl.width = 4 + timed;
3972     tbl.flags = 0;
3973     tbl.flags = TABLE_NO_FREE;
3974     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3975     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3976     tbl.contents[0][0] = "Mask";
3977     tbl.contents[0][1] = "Set By";
3978     tbl.contents[0][2] = "Triggered";
3979     if(timed)
3980     {
3981         tbl.contents[0][3] = "Expires";
3982         tbl.contents[0][4] = "Reason";
3983     }
3984     else
3985         tbl.contents[0][3] = "Reason";
3986     if(!matches)
3987     {
3988         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3989         reply("MSG_NONE");
3990         free(tbl.contents[0]);
3991         free(tbl.contents);
3992         return 0;
3993     }
3994
3995     msg_never = user_find_message(user, "MSG_NEVER");
3996     for(ii = 0; ii < matches; )
3997     {
3998         ban = bans[ii];
3999
4000         if(!timed)
4001             expires = "";
4002         else if(ban->expires)
4003             expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4004         else
4005             expires = msg_never;
4006
4007         if(ban->triggered)
4008             triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4009         else
4010             triggered = msg_never;
4011
4012         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4013         tbl.contents[ii][0] = ban->mask;
4014         tbl.contents[ii][1] = ban->owner;
4015         tbl.contents[ii][2] = strdup(triggered);
4016         if(timed)
4017         {
4018             tbl.contents[ii][3] = strdup(expires);
4019             tbl.contents[ii][4] = ban->reason;
4020         }
4021         else
4022             tbl.contents[ii][3] = ban->reason;
4023     }
4024     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4025     reply("MSG_MATCH_COUNT", matches);
4026     for(ii = 1; ii < tbl.length; ++ii)
4027     {
4028         free((char*)tbl.contents[ii][2]);
4029         if(timed)
4030             free((char*)tbl.contents[ii][3]);
4031         free(tbl.contents[ii]);
4032     }
4033     free(tbl.contents[0]);
4034     free(tbl.contents);
4035     return 1;
4036 }
4037
4038 static int
4039 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4040 {
4041     struct chanData *cData = channel->channel_info;
4042     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4043         return 0;
4044     if(cData->topic_mask)
4045         return !match_ircglob(new_topic, cData->topic_mask);
4046     else if(cData->topic)
4047         return irccasecmp(new_topic, cData->topic);
4048     else
4049         return 0;
4050 }
4051
4052 static CHANSERV_FUNC(cmd_topic)
4053 {
4054     struct chanData *cData;
4055     char *topic;
4056
4057     cData = channel->channel_info;
4058     if(argc < 2)
4059     {
4060         if(cData->topic)
4061         {
4062             SetChannelTopic(channel, chanserv, cData->topic, 1);
4063             reply("CSMSG_TOPIC_SET", cData->topic);
4064             return 1;
4065         }
4066
4067         reply("CSMSG_NO_TOPIC", channel->name);
4068         return 0;
4069     }
4070
4071     topic = unsplit_string(argv + 1, argc - 1, NULL);
4072     /* If they say "!topic *", use an empty topic. */
4073     if((topic[0] == '*') && (topic[1] == 0))
4074         topic[0] = 0;
4075     if(bad_topic(channel, user, topic))
4076     {
4077         char *topic_mask = cData->topic_mask;
4078         if(topic_mask)
4079         {
4080             char new_topic[TOPICLEN+1], tchar;
4081             int pos=0, starpos=-1, dpos=0, len;
4082
4083             while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4084             {
4085                 switch(tchar)
4086                 {
4087                 case '*':
4088                     if(starpos != -1)
4089                         goto bad_mask;
4090                     len = strlen(topic);
4091                     if((dpos + len) > TOPICLEN)
4092                         len = TOPICLEN + 1 - dpos;
4093                     memcpy(new_topic+dpos, topic, len);
4094                     dpos += len;
4095                     starpos = pos;
4096                     break;
4097                 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4098                 default: new_topic[dpos++] = tchar; break;
4099                 }
4100             }
4101             if((dpos > TOPICLEN) || tchar)
4102             {
4103             bad_mask:
4104                 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4105                 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4106                 return 0;
4107             }
4108             new_topic[dpos] = 0;
4109             SetChannelTopic(channel, chanserv, new_topic, 1);
4110         } else {
4111             reply("CSMSG_TOPIC_LOCKED", channel->name);
4112             return 0;
4113         }
4114     }
4115     else
4116         SetChannelTopic(channel, chanserv, topic, 1);
4117
4118     if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4119     {
4120         /* Grab the topic and save it as the default topic. */
4121         free(cData->topic);
4122         cData->topic = strdup(channel->topic);
4123     }
4124
4125     return 1;
4126 }
4127
4128 static CHANSERV_FUNC(cmd_mode)
4129 {
4130     struct userData *uData;
4131     struct mod_chanmode *change;
4132     short base_oplevel;
4133
4134     if(argc < 2)
4135     {
4136         change = &channel->channel_info->modes;
4137         if(change->modes_set || change->modes_clear) {
4138             modcmd_chanmode_announce(change);
4139             reply("CSMSG_DEFAULTED_MODES", channel->name);
4140         } else
4141             reply("CSMSG_NO_MODES", channel->name);
4142         return 1;
4143     }
4144
4145     uData = GetChannelUser(channel->channel_info, user->handle_info);
4146     if (!uData)
4147         base_oplevel = MAXOPLEVEL;
4148     else if (uData->access >= UL_OWNER)
4149         base_oplevel = 1;
4150     else
4151         base_oplevel = 1 + UL_OWNER - uData->access;
4152     change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4153     if(!change)
4154     {
4155         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4156         return 0;
4157     }
4158
4159     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4160        && mode_lock_violated(&channel->channel_info->modes, change))
4161     {
4162         char modes[MAXLEN];
4163         mod_chanmode_format(&channel->channel_info->modes, modes);
4164         reply("CSMSG_MODE_LOCKED", modes, channel->name);
4165         return 0;
4166     }
4167
4168     modcmd_chanmode_announce(change);
4169     mod_chanmode_free(change);
4170     reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4171     return 1;
4172 }
4173
4174 static CHANSERV_FUNC(cmd_invite)
4175 {
4176     struct userData *uData;
4177     struct userNode *invite;
4178
4179     uData = GetChannelUser(channel->channel_info, user->handle_info);
4180
4181     if(argc > 1)
4182     {
4183         if(!(invite = GetUserH(argv[1])))
4184         {
4185             reply("MSG_NICK_UNKNOWN", argv[1]);
4186             return 0;
4187         }
4188     }
4189     else
4190         invite = user;
4191
4192     if(GetUserMode(channel, invite))
4193     {
4194         reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4195         return 0;
4196     }
4197
4198     if(user != invite)
4199     {
4200         if(argc > 2)
4201         {
4202             char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4203             send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4204         }
4205         else
4206             send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4207     }
4208     irc_invite(chanserv, invite, channel);
4209     if(argc > 1)
4210         reply("CSMSG_INVITED_USER", argv[1], channel->name);
4211
4212     return 1;
4213 }
4214
4215 static CHANSERV_FUNC(cmd_inviteme)
4216 {
4217     if(GetUserMode(channel, user))
4218     {
4219         reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4220         return 0;
4221     }
4222     if(channel->channel_info
4223        && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4224     {
4225         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4226         return 0;
4227     }
4228     irc_invite(cmd->parent->bot, user, channel);
4229     return 1;
4230 }
4231
4232 static void
4233 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4234 {
4235     unsigned int combo;
4236     char buf1[INTERVALLEN], buf2[INTERVALLEN];
4237
4238     /* We display things based on two dimensions:
4239      * - Issue time: present or absent
4240      * - Expiration: revoked, expired, expires in future, or indefinite expiration
4241      * (in order of precedence, so something both expired and revoked
4242      * only counts as revoked)
4243      */
4244     combo = (suspended->issued ? 4 : 0)
4245         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4246     switch(combo) {
4247     case 0: /* no issue time, indefinite expiration */
4248         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4249         break;
4250     case 1: /* no issue time, expires in future */
4251         intervalString(buf1, suspended->expires-now, user->handle_info);
4252         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4253         break;
4254     case 2: /* no issue time, expired */
4255         intervalString(buf1, now-suspended->expires, user->handle_info);
4256         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4257         break;
4258     case 3: /* no issue time, revoked */
4259         intervalString(buf1, now-suspended->revoked, user->handle_info);
4260         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4261         break;
4262     case 4: /* issue time set, indefinite expiration */
4263         intervalString(buf1, now-suspended->issued, user->handle_info);
4264         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4265         break;
4266     case 5: /* issue time set, expires in future */
4267         intervalString(buf1, now-suspended->issued, user->handle_info);
4268         intervalString(buf2, suspended->expires-now, user->handle_info);
4269         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4270         break;
4271     case 6: /* issue time set, expired */
4272         intervalString(buf1, now-suspended->issued, user->handle_info);
4273         intervalString(buf2, now-suspended->expires, user->handle_info);
4274         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4275         break;
4276     case 7: /* issue time set, revoked */
4277         intervalString(buf1, now-suspended->issued, user->handle_info);
4278         intervalString(buf2, now-suspended->revoked, user->handle_info);
4279         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4280         break;
4281     default:
4282         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4283         return;
4284     }
4285 }
4286
4287 static CHANSERV_FUNC(cmd_info)
4288 {
4289     char modes[MAXLEN], buffer[INTERVALLEN];
4290     struct userData *uData, *owner;
4291     struct chanData *cData;
4292     struct do_not_register *dnr;
4293     struct note *note;
4294     dict_iterator_t it;
4295     int privileged;
4296
4297     cData = channel->channel_info;
4298     reply("CSMSG_CHANNEL_INFO", channel->name);
4299
4300     uData = GetChannelUser(cData, user->handle_info);
4301     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4302     {
4303         mod_chanmode_format(&cData->modes, modes);
4304         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4305         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4306     }
4307
4308     for(it = dict_first(cData->notes); it; it = iter_next(it))
4309     {
4310         int padding;
4311
4312         note = iter_data(it);
4313         if(!note_type_visible_to_user(cData, note->type, user))
4314             continue;
4315
4316         padding = PADLEN - 1 - strlen(iter_key(it));
4317         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4318     }
4319
4320     reply("CSMSG_CHANNEL_MAX", cData->max);
4321     for(owner = cData->users; owner; owner = owner->next)
4322         if(owner->access == UL_OWNER)
4323             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4324     reply("CSMSG_CHANNEL_USERS", cData->userCount);
4325     reply("CSMSG_CHANNEL_BANS", cData->banCount);
4326     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4327
4328     privileged = IsStaff(user);
4329     if(privileged)
4330         reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4331     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4332         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4333
4334     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4335         chanserv_show_dnrs(user, cmd, channel->name, NULL);
4336
4337     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4338     {
4339         struct suspended *suspended;
4340         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4341         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4342             show_suspension_info(cmd, user, suspended);
4343     }
4344     else if(IsSuspended(cData))
4345     {
4346         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4347         show_suspension_info(cmd, user, cData->suspended);
4348     }
4349     return 1;
4350 }
4351
4352 static CHANSERV_FUNC(cmd_netinfo)
4353 {
4354     extern time_t boot_time;
4355     extern unsigned long burst_length;
4356     char interval[INTERVALLEN];
4357
4358     reply("CSMSG_NETWORK_INFO");
4359     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4360     reply("CSMSG_NETWORK_USERS", dict_size(clients));
4361     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4362     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4363     reply("CSMSG_NETWORK_BANS", banCount);
4364     reply("CSMSG_NETWORK_CHANUSERS", userCount);
4365     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4366     reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4367     return 1;
4368 }
4369
4370 static void
4371 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4372 {
4373     struct helpfile_table table;
4374     unsigned int nn;
4375     struct userNode *user;
4376     char *nick;
4377
4378     table.length = 0;
4379     table.width = 1;
4380     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4381     table.contents = alloca(list->used*sizeof(*table.contents));
4382     for(nn=0; nn<list->used; nn++)
4383     {
4384         user = list->list[nn];
4385         if(user->modes & skip_flags)
4386             continue;
4387         if(IsBot(user))
4388             continue;
4389         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4390         if(IsAway(user))
4391         {
4392             nick = alloca(strlen(user->nick)+3);
4393             sprintf(nick, "(%s)", user->nick);
4394         }
4395         else
4396             nick = user->nick;
4397         table.contents[table.length][0] = nick;
4398         table.length++;
4399     }
4400     table_send(chanserv, to->nick, 0, NULL, table);
4401 }
4402
4403 static CHANSERV_FUNC(cmd_ircops)
4404 {
4405     reply("CSMSG_STAFF_OPERS");
4406     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4407     return 1;
4408 }
4409
4410 static CHANSERV_FUNC(cmd_helpers)
4411 {
4412     reply("CSMSG_STAFF_HELPERS");
4413     send_staff_list(user, &curr_helpers, FLAGS_OPER);
4414     return 1;
4415 }
4416
4417 static CHANSERV_FUNC(cmd_staff)
4418 {
4419     reply("CSMSG_NETWORK_STAFF");
4420     cmd_ircops(CSFUNC_ARGS);
4421     cmd_helpers(CSFUNC_ARGS);
4422     return 1;
4423 }
4424
4425 static CHANSERV_FUNC(cmd_peek)
4426 {
4427     struct modeNode *mn;
4428     char modes[MODELEN];
4429     unsigned int n;
4430     struct helpfile_table table;
4431
4432     irc_make_chanmode(channel, modes);
4433
4434     reply("CSMSG_PEEK_INFO", channel->name);
4435     reply("CSMSG_PEEK_TOPIC", channel->topic);
4436     reply("CSMSG_PEEK_MODES", modes);
4437     reply("CSMSG_PEEK_USERS", channel->members.used);
4438
4439     table.length = 0;
4440     table.width = 1;
4441     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4442     table.contents = alloca(channel->members.used*sizeof(*table.contents));
4443     for(n = 0; n < channel->members.used; n++)
4444     {
4445         mn = channel->members.list[n];
4446         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4447             continue;
4448         table.contents[table.length] = alloca(sizeof(**table.contents));
4449         table.contents[table.length][0] = mn->user->nick;
4450         table.length++;
4451     }
4452     if(table.length)
4453     {
4454         reply("CSMSG_PEEK_OPS");
4455         table_send(chanserv, user->nick, 0, NULL, table);
4456     }
4457     else
4458         reply("CSMSG_PEEK_NO_OPS");
4459     return 1;
4460 }
4461
4462 static MODCMD_FUNC(cmd_wipeinfo)
4463 {
4464     struct handle_info *victim;
4465     struct userData *ud, *actor, *real_actor;
4466     unsigned int override = 0;
4467
4468     REQUIRE_PARAMS(2);
4469     actor = GetChannelUser(channel->channel_info, user->handle_info);
4470     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4471     if(!(victim = modcmd_get_handle_info(user, argv[1])))
4472         return 0;
4473     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4474     {
4475         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4476         return 0;
4477     }
4478     if((ud->access >= actor->access) && (ud != actor))
4479     {
4480         reply("MSG_USER_OUTRANKED", victim->handle);
4481         return 0;
4482     }
4483     if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4484         override = CMD_LOG_OVERRIDE;
4485     if(ud->info)
4486         free(ud->info);
4487     ud->info = NULL;
4488     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4489     return 1 | override;
4490 }
4491
4492 static CHANSERV_FUNC(cmd_resync)
4493 {
4494     struct mod_chanmode *changes;
4495     struct chanData *cData = channel->channel_info;
4496     unsigned int ii, used;
4497
4498     changes = mod_chanmode_alloc(channel->members.used * 2);
4499     for(ii = used = 0; ii < channel->members.used; ++ii)
4500     {
4501         struct modeNode *mn = channel->members.list[ii];
4502         struct userData *uData;
4503
4504         if(IsService(mn->user))
4505             continue;
4506
4507         uData = GetChannelAccess(cData, mn->user->handle_info);
4508         if(!cData->lvlOpts[lvlGiveOps]
4509            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4510         {
4511             if(!(mn->modes & MODE_CHANOP))
4512             {
4513                 changes->args[used].mode = MODE_CHANOP;
4514                 changes->args[used++].u.member = mn;
4515             }
4516         }
4517         else if(!cData->lvlOpts[lvlGiveVoice]
4518                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4519         {
4520             if(mn->modes & MODE_CHANOP)
4521             {
4522                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4523                 changes->args[used++].u.member = mn;
4524             }
4525             if(!(mn->modes & MODE_VOICE))
4526             {
4527                 changes->args[used].mode = MODE_VOICE;
4528                 changes->args[used++].u.member = mn;
4529             }
4530         }
4531         else
4532         {
4533             if(mn->modes)
4534             {
4535                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4536                 changes->args[used++].u.member = mn;
4537             }
4538         }
4539     }
4540     changes->argc = used;
4541     modcmd_chanmode_announce(changes);
4542     mod_chanmode_free(changes);
4543     reply("CSMSG_RESYNCED_USERS", channel->name);
4544     return 1;
4545 }
4546
4547 static CHANSERV_FUNC(cmd_seen)
4548 {
4549     struct userData *uData;
4550     struct handle_info *handle;
4551     char seen[INTERVALLEN];
4552
4553     REQUIRE_PARAMS(2);
4554
4555     if(!irccasecmp(argv[1], chanserv->nick))
4556     {
4557         reply("CSMSG_IS_CHANSERV");
4558         return 1;
4559     }
4560
4561     if(!(handle = get_handle_info(argv[1])))
4562     {
4563         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4564         return 0;
4565     }
4566
4567     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4568     {
4569         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4570         return 0;
4571     }
4572
4573     if(uData->present)
4574         reply("CSMSG_USER_PRESENT", handle->handle);
4575     else if(uData->seen)
4576         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4577     else
4578         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4579
4580     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4581         reply("CSMSG_USER_VACATION", handle->handle);
4582
4583     return 1;
4584 }
4585
4586 static MODCMD_FUNC(cmd_names)
4587 {
4588     struct userNode *targ;
4589     struct userData *targData;
4590     unsigned int ii, pos;
4591     char buf[400];
4592
4593     for(ii=pos=0; ii<channel->members.used; ++ii)
4594     {
4595         targ = channel->members.list[ii]->user;
4596         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4597         if(!targData)
4598             continue;
4599         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4600         {
4601             buf[pos] = 0;
4602             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4603             pos = 0;
4604         }
4605         buf[pos++] = ' ';
4606         if(IsUserSuspended(targData))
4607             buf[pos++] = 's';
4608         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4609     }
4610     buf[pos] = 0;
4611     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4612     reply("CSMSG_END_NAMES", channel->name);
4613     return 1;
4614 }
4615
4616 static int
4617 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4618 {
4619     switch(ntype->visible_type)
4620     {
4621     case NOTE_VIS_ALL: return 1;
4622     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4623     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4624     }
4625 }
4626
4627 static int
4628 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4629 {
4630     struct userData *uData;
4631
4632     switch(ntype->set_access_type)
4633     {
4634     case NOTE_SET_CHANNEL_ACCESS:
4635         if(!user->handle_info)
4636             return 0;
4637         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4638             return 0;
4639         return uData->access >= ntype->set_access.min_ulevel;
4640     case NOTE_SET_CHANNEL_SETTER:
4641         return check_user_level(channel, user, lvlSetters, 1, 0);
4642     case NOTE_SET_PRIVILEGED: default:
4643         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4644     }
4645 }
4646
4647 static CHANSERV_FUNC(cmd_note)
4648 {
4649     struct chanData *cData;
4650     struct note *note;
4651     struct note_type *ntype;
4652
4653     cData = channel->channel_info;
4654     if(!cData)
4655     {
4656         reply("CSMSG_NOT_REGISTERED", channel->name);
4657         return 0;
4658     }
4659
4660     /* If no arguments, show all visible notes for the channel. */
4661     if(argc < 2)
4662     {
4663         dict_iterator_t it;
4664         unsigned int count;
4665
4666         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4667         {
4668             note = iter_data(it);
4669             if(!note_type_visible_to_user(cData, note->type, user))
4670                 continue;
4671             if(!count++)
4672                 reply("CSMSG_NOTELIST_HEADER", channel->name);
4673             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4674         }
4675         if(count)
4676             reply("CSMSG_NOTELIST_END", channel->name);
4677         else
4678             reply("CSMSG_NOTELIST_EMPTY", channel->name);
4679     }
4680     /* If one argument, show the named note. */
4681     else if(argc == 2)
4682     {
4683         if((note = dict_find(cData->notes, argv[1], NULL))
4684            && note_type_visible_to_user(cData, note->type, user))
4685         {
4686             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4687         }
4688         else if((ntype = dict_find(note_types, argv[1], NULL))
4689                 && note_type_visible_to_user(NULL, ntype, user))
4690         {
4691             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4692             return 0;
4693         }
4694         else
4695         {
4696             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4697             return 0;
4698         }
4699     }
4700     /* Assume they're trying to set a note. */
4701     else
4702     {
4703         char *note_text;
4704         ntype = dict_find(note_types, argv[1], NULL);
4705         if(!ntype)
4706         {
4707             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4708             return 0;
4709         }
4710         else if(note_type_settable_by_user(channel, ntype, user))
4711         {
4712             note_text = unsplit_string(argv+2, argc-2, NULL);
4713             if((note = dict_find(cData->notes, argv[1], NULL)))
4714                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4715             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4716             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4717
4718             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4719             {
4720                 /* The note is viewable to staff only, so return 0
4721                    to keep the invocation from getting logged (or
4722                    regular users can see it in !events). */
4723                 return 0;
4724             }
4725         }
4726         else
4727         {
4728             reply("CSMSG_NO_ACCESS");
4729             return 0;
4730         }
4731     }
4732     return 1;
4733 }
4734
4735 static CHANSERV_FUNC(cmd_delnote)
4736 {
4737     struct note *note;
4738
4739     REQUIRE_PARAMS(2);
4740     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4741        || !note_type_settable_by_user(channel, note->type, user))
4742     {
4743         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4744         return 0;
4745     }
4746     dict_remove(channel->channel_info->notes, note->type->name);
4747     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4748     return 1;
4749 }
4750
4751 static CHANSERV_FUNC(cmd_events)
4752 {
4753     struct logSearch discrim;
4754     struct logReport report;
4755     unsigned int matches, limit;
4756
4757     limit = (argc > 1) ? atoi(argv[1]) : 10;
4758     if(limit < 1 || limit > 200)
4759         limit = 10;
4760
4761     memset(&discrim, 0, sizeof(discrim));
4762     discrim.masks.bot = chanserv;
4763     discrim.masks.channel_name = channel->name;
4764     if(argc > 2)
4765         discrim.masks.command = argv[2];
4766     discrim.limit = limit;
4767     discrim.max_time = INT_MAX;
4768     discrim.severities = 1 << LOG_COMMAND;
4769     report.reporter = chanserv;
4770     report.user = user;
4771     reply("CSMSG_EVENT_SEARCH_RESULTS");
4772     matches = log_entry_search(&discrim, log_report_entry, &report);
4773     if(matches)
4774         reply("MSG_MATCH_COUNT", matches);
4775     else
4776         reply("MSG_NO_MATCHES");
4777     return 1;
4778 }
4779
4780 static CHANSERV_FUNC(cmd_say)
4781 {
4782     char *msg;
4783     if(channel)
4784     {
4785         REQUIRE_PARAMS(2);
4786         msg = unsplit_string(argv + 1, argc - 1, NULL);
4787         send_channel_message(channel, cmd->parent->bot, "%s", msg);
4788     }
4789     else if(GetUserH(argv[1]))
4790     {
4791         REQUIRE_PARAMS(3);
4792         msg = unsplit_string(argv + 2, argc - 2, NULL);
4793         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4794     }
4795     else
4796     {
4797         reply("MSG_NOT_TARGET_NAME");
4798         return 0;
4799     }
4800     return 1;
4801 }
4802
4803 static CHANSERV_FUNC(cmd_emote)
4804 {
4805     char *msg;
4806     assert(argc >= 2);
4807     if(channel)
4808     {
4809         /* CTCP is so annoying. */
4810         msg = unsplit_string(argv + 1, argc - 1, NULL);
4811         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4812     }
4813     else if(GetUserH(argv[1]))
4814     {
4815         msg = unsplit_string(argv + 2, argc - 2, NULL);
4816         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4817     }
4818     else
4819     {
4820         reply("MSG_NOT_TARGET_NAME");
4821         return 0;
4822     }
4823     return 1;
4824 }
4825
4826 struct channelList *
4827 chanserv_support_channels(void)
4828 {
4829     return &chanserv_conf.support_channels;
4830 }
4831
4832 static CHANSERV_FUNC(cmd_expire)
4833 {
4834     int channel_count = registered_channels;
4835     expire_channels(NULL);
4836     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4837     return 1;
4838 }
4839
4840 static void
4841 chanserv_expire_suspension(void *data)
4842 {
4843     struct suspended *suspended = data;
4844     struct chanNode *channel;
4845
4846     if(!suspended->expires || (now < suspended->expires))
4847         suspended->revoked = now;
4848     channel = suspended->cData->channel;
4849     suspended->cData->channel = channel;
4850     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4851     if(!IsOffChannel(suspended->cData))
4852     {
4853         struct mod_chanmode change;
4854         mod_chanmode_init(&change);
4855         change.argc = 1;
4856         change.args[0].mode = MODE_CHANOP;
4857         change.args[0].u.member = AddChannelUser(chanserv, channel);
4858         mod_chanmode_announce(chanserv, channel, &change);
4859     }
4860 }
4861
4862 static CHANSERV_FUNC(cmd_csuspend)
4863 {
4864     struct suspended *suspended;
4865     char reason[MAXLEN];
4866     time_t expiry, duration;
4867     struct userData *uData;
4868
4869     REQUIRE_PARAMS(3);
4870
4871     if(IsProtected(channel->channel_info))
4872     {
4873         reply("CSMSG_SUSPEND_NODELETE", channel->name);
4874         return 0;
4875     }
4876
4877     if(argv[1][0] == '!')
4878         argv[1]++;
4879     else if(IsSuspended(channel->channel_info))
4880     {
4881         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4882         show_suspension_info(cmd, user, channel->channel_info->suspended);
4883         return 0;
4884     }
4885
4886     if(!strcmp(argv[1], "0"))
4887         expiry = 0;
4888     else if((duration = ParseInterval(argv[1])))
4889         expiry = now + duration;
4890     else
4891     {
4892         reply("MSG_INVALID_DURATION", argv[1]);
4893         return 0;
4894     }
4895
4896     unsplit_string(argv + 2, argc - 2, reason);
4897
4898     suspended = calloc(1, sizeof(*suspended));
4899     suspended->revoked = 0;
4900     suspended->issued = now;
4901     suspended->suspender = strdup(user->handle_info->handle);
4902     suspended->expires = expiry;
4903     suspended->reason = strdup(reason);
4904     suspended->cData = channel->channel_info;
4905     suspended->previous = suspended->cData->suspended;
4906     suspended->cData->suspended = suspended;
4907
4908     if(suspended->expires)
4909         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4910
4911     if(IsSuspended(channel->channel_info))
4912     {
4913         suspended->previous->revoked = now;
4914         if(suspended->previous->expires)
4915             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4916         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4917         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4918     }
4919     else
4920     {
4921         /* Mark all users in channel as absent. */
4922         for(uData = channel->channel_info->users; uData; uData = uData->next)
4923         {
4924             if(uData->present)
4925             {
4926                 uData->seen = now;
4927                 uData->present = 0;
4928             }
4929         }
4930
4931         /* Mark the channel as suspended, then part. */
4932         channel->channel_info->flags |= CHANNEL_SUSPENDED;
4933         DelChannelUser(chanserv, channel, suspended->reason, 0);
4934         reply("CSMSG_SUSPENDED", channel->name);
4935         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4936         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4937     }
4938     return 1;
4939 }
4940
4941 static CHANSERV_FUNC(cmd_cunsuspend)
4942 {
4943     struct suspended *suspended;
4944     char message[MAXLEN];
4945
4946     if(!IsSuspended(channel->channel_info))
4947     {
4948         reply("CSMSG_NOT_SUSPENDED", channel->name);
4949         return 0;
4950     }
4951
4952     suspended = channel->channel_info->suspended;
4953
4954     /* Expire the suspension and join ChanServ to the channel. */
4955     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4956     chanserv_expire_suspension(suspended);
4957     reply("CSMSG_UNSUSPENDED", channel->name);
4958     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4959     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4960     return 1;
4961 }
4962
4963 typedef struct chanservSearch
4964 {
4965     char *name;
4966     char *registrar;
4967
4968     time_t unvisited;
4969     time_t registered;
4970
4971     unsigned long flags;
4972     unsigned int limit;
4973 } *search_t;
4974
4975 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4976
4977 static search_t
4978 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4979 {
4980     search_t search;
4981     unsigned int i;
4982
4983     search = malloc(sizeof(struct chanservSearch));
4984     memset(search, 0, sizeof(*search));
4985     search->limit = 25;
4986
4987     for(i = 0; i < argc; i++)
4988     {
4989         /* Assume all criteria require arguments. */
4990         if(i == (argc - 1))
4991         {
4992             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4993             goto fail;
4994         }
4995
4996         if(!irccasecmp(argv[i], "name"))
4997             search->name = argv[++i];
4998         else if(!irccasecmp(argv[i], "registrar"))
4999             search->registrar = argv[++i];
5000         else if(!irccasecmp(argv[i], "unvisited"))
5001             search->unvisited = ParseInterval(argv[++i]);
5002         else if(!irccasecmp(argv[i], "registered"))
5003             search->registered = ParseInterval(argv[++i]);
5004         else if(!irccasecmp(argv[i], "flags"))
5005         {
5006             i++;
5007             if(!irccasecmp(argv[i], "nodelete"))
5008                 search->flags |= CHANNEL_NODELETE;
5009             else if(!irccasecmp(argv[i], "suspended"))
5010                 search->flags |= CHANNEL_SUSPENDED;
5011             else
5012             {
5013                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5014                 goto fail;
5015             }
5016         }
5017         else if(!irccasecmp(argv[i], "limit"))
5018             search->limit = strtoul(argv[++i], NULL, 10);
5019         else
5020         {
5021             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5022             goto fail;
5023         }
5024     }
5025
5026     if(search->name && !strcmp(search->name, "*"))
5027         search->name = 0;
5028     if(search->registrar && !strcmp(search->registrar, "*"))
5029         search->registrar = 0;
5030
5031     return search;
5032   fail:
5033     free(search);
5034     return NULL;
5035 }
5036
5037 static int
5038 chanserv_channel_match(struct chanData *channel, search_t search)
5039 {
5040     const char *name = channel->channel->name;
5041     if((search->name && !match_ircglob(name, search->name)) ||
5042        (search->registrar && !channel->registrar) ||
5043        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5044        (search->unvisited && (now - channel->visited) < search->unvisited) ||
5045        (search->registered && (now - channel->registered) > search->registered) ||
5046        (search->flags && ((search->flags & channel->flags) != search->flags)))
5047         return 0;
5048
5049     return 1;
5050 }
5051
5052 static unsigned int
5053 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5054 {
5055     struct chanData *channel;
5056     unsigned int matches = 0;
5057
5058     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5059     {
5060         if(!chanserv_channel_match(channel, search))
5061             continue;
5062         matches++;
5063         smf(channel, data);
5064     }
5065
5066     return matches;
5067 }
5068
5069 static void
5070 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5071 {
5072 }
5073
5074 static void
5075 search_print(struct chanData *channel, void *data)
5076 {
5077     send_message_type(4, data, chanserv, "%s", channel->channel->name);
5078 }
5079
5080 static CHANSERV_FUNC(cmd_search)
5081 {
5082     search_t search;
5083     unsigned int matches;
5084     channel_search_func action;
5085
5086     REQUIRE_PARAMS(3);
5087
5088     if(!irccasecmp(argv[1], "count"))
5089         action = search_count;
5090     else if(!irccasecmp(argv[1], "print"))
5091         action = search_print;
5092     else
5093     {
5094         reply("CSMSG_ACTION_INVALID", argv[1]);
5095         return 0;
5096     }
5097
5098     search = chanserv_search_create(user, argc - 2, argv + 2);
5099     if(!search)
5100         return 0;
5101
5102     if(action == search_count)
5103         search->limit = INT_MAX;
5104
5105     if(action == search_print)
5106         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5107
5108     matches = chanserv_channel_search(search, action, user);
5109
5110     if(matches)
5111         reply("MSG_MATCH_COUNT", matches);
5112     else
5113         reply("MSG_NO_MATCHES");
5114
5115     free(search);
5116     return 1;
5117 }
5118
5119 static CHANSERV_FUNC(cmd_unvisited)
5120 {
5121     struct chanData *cData;
5122     time_t interval = chanserv_conf.channel_expire_delay;
5123     char buffer[INTERVALLEN];
5124     unsigned int limit = 25, matches = 0;
5125
5126     if(argc > 1)
5127     {
5128         interval = ParseInterval(argv[1]);
5129         if(argc > 2)
5130             limit = atoi(argv[2]);
5131     }
5132
5133     intervalString(buffer, interval, user->handle_info);
5134     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5135
5136     for(cData = channelList; cData && matches < limit; cData = cData->next)
5137     {
5138         if((now - cData->visited) < interval)
5139             continue;
5140
5141         intervalString(buffer, now - cData->visited, user->handle_info);
5142         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5143         matches++;
5144     }
5145
5146     return 1;
5147 }
5148
5149 static MODCMD_FUNC(chan_opt_defaulttopic)
5150 {
5151     if(argc > 1)
5152     {
5153         char *topic;
5154
5155         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5156         {
5157             reply("CSMSG_TOPIC_LOCKED", channel->name);
5158             return 0;
5159         }
5160
5161         topic = unsplit_string(argv+1, argc-1, NULL);
5162
5163         free(channel->channel_info->topic);
5164         if(topic[0] == '*' && topic[1] == 0)
5165         {
5166             topic = channel->channel_info->topic = NULL;
5167         }
5168         else
5169         {
5170             topic = channel->channel_info->topic = strdup(topic);
5171             if(channel->channel_info->topic_mask
5172                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5173                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5174         }
5175         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5176     }
5177
5178     if(channel->channel_info->topic)
5179         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5180     else
5181         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5182     return 1;
5183 }
5184
5185 static MODCMD_FUNC(chan_opt_topicmask)
5186 {
5187     if(argc > 1)
5188     {
5189         struct chanData *cData = channel->channel_info;
5190         char *mask;
5191
5192         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5193         {
5194             reply("CSMSG_TOPIC_LOCKED", channel->name);
5195             return 0;
5196         }
5197
5198         mask = unsplit_string(argv+1, argc-1, NULL);
5199
5200         if(cData->topic_mask)
5201             free(cData->topic_mask);
5202         if(mask[0] == '*' && mask[1] == 0)
5203         {
5204             cData->topic_mask = 0;
5205         }
5206         else
5207         {
5208             cData->topic_mask = strdup(mask);
5209             if(!cData->topic)
5210                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5211             else if(!match_ircglob(cData->topic, cData->topic_mask))
5212                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5213         }
5214     }
5215
5216     if(channel->channel_info->topic_mask)
5217         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5218     else
5219         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5220     return 1;
5221 }
5222
5223 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5224 {
5225     if(argc > 1)
5226     {
5227         char *greeting = unsplit_string(argv+1, argc-1, NULL);
5228         char *previous;
5229
5230         previous = *data;
5231         if(greeting[0] == '*' && greeting[1] == 0)
5232             *data = NULL;
5233         else
5234         {
5235             unsigned int length = strlen(greeting);
5236             if(length > chanserv_conf.greeting_length)
5237             {
5238                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5239                 return 0;
5240             }
5241             *data = strdup(greeting);
5242         }
5243         if(previous)
5244             free(previous);
5245     }
5246
5247     if(*data)
5248         reply(name, *data);
5249     else
5250         reply(name, user_find_message(user, "MSG_NONE"));
5251     return 1;
5252 }
5253
5254 static MODCMD_FUNC(chan_opt_greeting)
5255 {
5256     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5257 }
5258
5259 static MODCMD_FUNC(chan_opt_usergreeting)
5260 {
5261     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5262 }
5263
5264 static MODCMD_FUNC(chan_opt_modes)
5265 {
5266     struct mod_chanmode *new_modes;
5267     char modes[MODELEN];
5268
5269     if(argc > 1)
5270     {
5271         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5272         {
5273             reply("CSMSG_NO_ACCESS");
5274             return 0;
5275         }
5276         if(argv[1][0] == '*' && argv[1][1] == 0)
5277         {
5278             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5279         }
5280         else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5281         {
5282             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5283             return 0;
5284         }
5285         else if(new_modes->argc > 1)
5286         {
5287             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5288             mod_chanmode_free(new_modes);
5289             return 0;
5290         }
5291         else
5292         {
5293             channel->channel_info->modes = *new_modes;
5294             modcmd_chanmode_announce(new_modes);
5295             mod_chanmode_free(new_modes);
5296         }
5297     }
5298
5299     mod_chanmode_format(&channel->channel_info->modes, modes);
5300     if(modes[0])
5301         reply("CSMSG_SET_MODES", modes);
5302     else
5303         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5304     return 1;
5305 }
5306
5307 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5308 static int
5309 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5310 {
5311     struct chanData *cData = channel->channel_info;
5312     int value;
5313
5314     if(argc > 1)
5315     {
5316         /* Set flag according to value. */
5317         if(enabled_string(argv[1]))
5318         {
5319             cData->flags |= mask;
5320             value = 1;
5321         }
5322         else if(disabled_string(argv[1]))
5323         {
5324             cData->flags &= ~mask;
5325             value = 0;
5326         }
5327         else
5328         {
5329             reply("MSG_INVALID_BINARY", argv[1]);
5330             return 0;
5331         }
5332     }
5333     else
5334     {
5335         /* Find current option value. */
5336         value = (cData->flags & mask) ? 1 : 0;
5337     }
5338
5339     if(value)
5340         reply(name, user_find_message(user, "MSG_ON"));
5341     else
5342         reply(name, user_find_message(user, "MSG_OFF"));
5343     return 1;
5344 }
5345
5346 static MODCMD_FUNC(chan_opt_nodelete)
5347 {
5348     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5349     {
5350         reply("MSG_SETTING_PRIVILEGED", argv[0]);
5351         return 0;
5352     }
5353
5354     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5355 }
5356
5357 static MODCMD_FUNC(chan_opt_dynlimit)
5358 {
5359     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5360 }
5361
5362 static MODCMD_FUNC(chan_opt_offchannel)
5363 {
5364     struct chanData *cData = channel->channel_info;
5365     int value;
5366
5367     if(argc > 1)
5368     {
5369         /* Set flag according to value. */
5370         if(enabled_string(argv[1]))
5371         {
5372             if(!IsOffChannel(cData))
5373                 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5374             cData->flags |= CHANNEL_OFFCHANNEL;
5375             value = 1;
5376         }
5377         else if(disabled_string(argv[1]))
5378         {
5379             if(IsOffChannel(cData))
5380             {
5381                 struct mod_chanmode change;
5382                 mod_chanmode_init(&change);
5383                 change.argc = 1;
5384                 change.args[0].mode = MODE_CHANOP;
5385                 change.args[0].u.member = AddChannelUser(chanserv, channel);
5386                 mod_chanmode_announce(chanserv, channel, &change);
5387             }
5388             cData->flags &= ~CHANNEL_OFFCHANNEL;
5389             value = 0;
5390         }
5391         else
5392         {
5393             reply("MSG_INVALID_BINARY", argv[1]);
5394             return 0;
5395         }
5396     }
5397     else
5398     {
5399         /* Find current option value. */
5400         value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5401     }
5402
5403     if(value)
5404         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5405     else
5406         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5407     return 1;
5408 }
5409
5410 static MODCMD_FUNC(chan_opt_defaults)
5411 {
5412     struct userData *uData;
5413     struct chanData *cData;
5414     const char *confirm;
5415     enum levelOption lvlOpt;
5416     enum charOption chOpt;
5417
5418     cData = channel->channel_info;
5419     uData = GetChannelUser(cData, user->handle_info);
5420     if(!uData || (uData->access < UL_OWNER))
5421     {
5422         reply("CSMSG_OWNER_DEFAULTS", channel->name);
5423         return 0;
5424     }
5425     confirm = make_confirmation_string(uData);
5426     if((argc < 2) || strcmp(argv[1], confirm))
5427     {
5428         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5429         return 0;
5430     }
5431     cData->flags = CHANNEL_DEFAULT_FLAGS;
5432     cData->modes = chanserv_conf.default_modes;
5433     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5434         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5435     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5436         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5437     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5438     return 1;
5439 }
5440
5441 static int
5442 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5443 {
5444     struct chanData *cData = channel->channel_info;
5445     struct userData *uData;
5446     unsigned short value;
5447
5448     if(argc > 1)
5449     {
5450         if(!check_user_level(channel, user, option, 1, 1))
5451         {
5452             reply("CSMSG_CANNOT_SET");
5453             return 0;
5454         }
5455         value = user_level_from_name(argv[1], UL_OWNER+1);
5456         if(!value && strcmp(argv[1], "0"))
5457         {
5458             reply("CSMSG_INVALID_ACCESS", argv[1]);
5459             return 0;
5460         }
5461         uData = GetChannelUser(cData, user->handle_info);
5462         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5463         {
5464             reply("CSMSG_BAD_SETLEVEL");
5465             return 0;
5466         }
5467         switch(option)
5468         {
5469         case lvlGiveVoice:
5470             if(value > cData->lvlOpts[lvlGiveOps])
5471             {
5472                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5473                 return 0;
5474             }
5475             break;
5476         case lvlGiveOps:
5477             if(value < cData->lvlOpts[lvlGiveVoice])
5478             {
5479                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5480                 return 0;
5481             }
5482             break;
5483         case lvlSetters:
5484             /* This test only applies to owners, since non-owners
5485              * trying to set an option to above their level get caught
5486              * by the CSMSG_BAD_SETLEVEL test above.
5487              */
5488             if(value > uData->access)
5489             {
5490                 reply("CSMSG_BAD_SETTERS");
5491                 return 0;
5492             }
5493             break;
5494         default:
5495             break;
5496         }
5497         cData->lvlOpts[option] = value;
5498     }
5499     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5500     return argc > 1;
5501 }
5502
5503 static MODCMD_FUNC(chan_opt_enfops)
5504 {
5505     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5506 }
5507
5508 static MODCMD_FUNC(chan_opt_giveops)
5509 {
5510     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5511 }
5512
5513 static MODCMD_FUNC(chan_opt_enfmodes)
5514 {
5515     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5516 }
5517
5518 static MODCMD_FUNC(chan_opt_enftopic)
5519 {
5520     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5521 }
5522
5523 static MODCMD_FUNC(chan_opt_pubcmd)
5524 {
5525     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5526 }
5527
5528 static MODCMD_FUNC(chan_opt_setters)
5529 {
5530     return channel_level_option(lvlSetters, CSFUNC_ARGS);
5531 }
5532
5533 static MODCMD_FUNC(chan_opt_ctcpusers)
5534 {
5535     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5536 }
5537
5538 static MODCMD_FUNC(chan_opt_userinfo)
5539 {
5540     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5541 }
5542
5543 static MODCMD_FUNC(chan_opt_givevoice)
5544 {
5545     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5546 }
5547
5548 static MODCMD_FUNC(chan_opt_topicsnarf)
5549 {
5550     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5551 }
5552
5553 static MODCMD_FUNC(chan_opt_inviteme)
5554 {
5555     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5556 }
5557
5558 static int
5559 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5560 {
5561     struct chanData *cData = channel->channel_info;
5562     int count = charOptions[option].count, index;
5563
5564     if(argc > 1)
5565     {
5566         index = atoi(argv[1]);
5567
5568         if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5569         {
5570             reply("CSMSG_INVALID_NUMERIC", index);
5571             /* Show possible values. */
5572             for(index = 0; index < count; index++)
5573                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5574             return 0;
5575         }
5576
5577         cData->chOpts[option] = charOptions[option].values[index].value;
5578     }
5579     else
5580     {
5581         /* Find current option value. */
5582       find_value:
5583         for(index = 0;
5584             (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5585             index++);
5586         if(index == count)
5587         {
5588             /* Somehow, the option value is corrupt; reset it to the default. */
5589             cData->chOpts[option] = charOptions[option].default_value;
5590             goto find_value;
5591         }
5592     }
5593
5594     reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5595     return 1;
5596 }
5597
5598 static MODCMD_FUNC(chan_opt_protect)
5599 {
5600     return channel_multiple_option(chProtect, CSFUNC_ARGS);
5601 }
5602
5603 static MODCMD_FUNC(chan_opt_toys)
5604 {
5605     return channel_multiple_option(chToys, CSFUNC_ARGS);
5606 }
5607
5608 static MODCMD_FUNC(chan_opt_ctcpreaction)
5609 {
5610     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5611 }
5612
5613 static MODCMD_FUNC(chan_opt_topicrefresh)
5614 {
5615     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5616 }
5617
5618 static struct svccmd_list set_shows_list;
5619
5620 static void
5621 handle_svccmd_unbind(struct svccmd *target) {
5622     unsigned int ii;
5623     for(ii=0; ii<set_shows_list.used; ++ii)
5624         if(target == set_shows_list.list[ii])
5625             set_shows_list.used = 0;
5626 }
5627
5628 static CHANSERV_FUNC(cmd_set)
5629 {
5630     struct svccmd *subcmd;
5631     char buf[MAXLEN];
5632     unsigned int ii;
5633
5634     /* Check if we need to (re-)initialize set_shows_list. */
5635     if(!set_shows_list.used)
5636     {
5637         if(!set_shows_list.size)
5638         {
5639             set_shows_list.size = chanserv_conf.set_shows->used;
5640             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5641         }
5642         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5643         {
5644             const char *name = chanserv_conf.set_shows->list[ii];
5645             sprintf(buf, "%s %s", argv[0], name);
5646             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5647             if(!subcmd)
5648             {
5649                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5650                 continue;
5651             }
5652             svccmd_list_append(&set_shows_list, subcmd);
5653         }
5654     }
5655
5656     if(argc < 2)
5657     {
5658         reply("CSMSG_CHANNEL_OPTIONS");
5659         for(ii = 0; ii < set_shows_list.used; ii++)
5660         {
5661             subcmd = set_shows_list.list[ii];
5662             subcmd->command->func(user, channel, 1, argv+1, subcmd);
5663         }
5664         return 1;
5665     }
5666
5667     sprintf(buf, "%s %s", argv[0], argv[1]);
5668     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5669     if(!subcmd)
5670     {
5671         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5672         return 0;
5673     }
5674     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5675     {
5676         reply("CSMSG_NO_ACCESS");
5677         return 0;
5678     }
5679
5680     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5681 }
5682
5683 static int
5684 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5685 {
5686     struct userData *uData;
5687
5688     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5689     if(!uData)
5690     {
5691         reply("CSMSG_NOT_USER", channel->name);
5692         return 0;
5693     }
5694
5695     if(argc < 2)
5696     {
5697         /* Just show current option value. */
5698     }
5699     else if(enabled_string(argv[1]))
5700     {
5701         uData->flags |= mask;
5702     }
5703     else if(disabled_string(argv[1]))
5704     {
5705         uData->flags &= ~mask;
5706     }
5707     else
5708     {
5709         reply("MSG_INVALID_BINARY", argv[1]);
5710         return 0;
5711     }
5712
5713     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5714     return 1;
5715 }
5716
5717 static MODCMD_FUNC(user_opt_noautoop)
5718 {
5719     struct userData *uData;
5720
5721     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5722     if(!uData)
5723     {
5724         reply("CSMSG_NOT_USER", channel->name);
5725         return 0;
5726     }
5727     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5728         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5729     else
5730         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5731 }
5732
5733 static MODCMD_FUNC(user_opt_autoinvite)
5734 {
5735     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5736 }
5737
5738 static MODCMD_FUNC(user_opt_info)
5739 {
5740     struct userData *uData;
5741     char *infoline;
5742
5743     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5744
5745     if(!uData)
5746     {
5747         /* If they got past the command restrictions (which require access)
5748          * but fail this test, we have some fool with security override on.
5749          */
5750         reply("CSMSG_NOT_USER", channel->name);
5751         return 0;
5752     }
5753
5754     if(argc > 1)
5755     {
5756         size_t bp;
5757         infoline = unsplit_string(argv + 1, argc - 1, NULL);
5758         if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5759         {
5760             reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5761             return 0;
5762         }
5763         bp = strcspn(infoline, "\001");
5764         if(infoline[bp])
5765         {
5766             reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5767             return 0;
5768         }
5769         if(uData->info)
5770             free(uData->info);
5771         if(infoline[0] == '*' && infoline[1] == 0)
5772             uData->info = NULL;
5773         else
5774             uData->info = strdup(infoline);
5775     }
5776     if(uData->info)
5777         reply("CSMSG_USET_INFO", uData->info);
5778     else
5779         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5780     return 1;
5781 }
5782
5783 struct svccmd_list uset_shows_list;
5784
5785 static CHANSERV_FUNC(cmd_uset)
5786 {
5787     struct svccmd *subcmd;
5788     char buf[MAXLEN];
5789     unsigned int ii;
5790
5791     /* Check if we need to (re-)initialize uset_shows_list. */
5792     if(!uset_shows_list.used)
5793     {
5794         char *options[] =
5795         {
5796             "NoAutoOp", "AutoInvite", "Info"
5797         };
5798
5799         if(!uset_shows_list.size)
5800         {
5801             uset_shows_list.size = ArrayLength(options);
5802             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5803         }
5804         for(ii = 0; ii < ArrayLength(options); ii++)
5805         {
5806             const char *name = options[ii];
5807             sprintf(buf, "%s %s", argv[0], name);
5808             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5809             if(!subcmd)
5810             {
5811                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5812                 continue;
5813             }
5814             svccmd_list_append(&uset_shows_list, subcmd);
5815         }
5816     }
5817
5818     if(argc < 2)
5819     {
5820         /* Do this so options are presented in a consistent order. */
5821         reply("CSMSG_USER_OPTIONS");
5822         for(ii = 0; ii < uset_shows_list.used; ii++)
5823             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5824         return 1;
5825     }
5826
5827     sprintf(buf, "%s %s", argv[0], argv[1]);
5828     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5829     if(!subcmd)
5830     {
5831         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5832         return 0;
5833     }
5834
5835     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5836 }
5837
5838 static CHANSERV_FUNC(cmd_giveownership)
5839 {
5840     struct handle_info *new_owner_hi;
5841     struct userData *new_owner;
5842     struct userData *curr_user;
5843     struct userData *invoker;
5844     struct chanData *cData = channel->channel_info;
5845     struct do_not_register *dnr;
5846     const char *confirm;
5847     unsigned int force;
5848     unsigned short co_access;
5849     char reason[MAXLEN];
5850
5851     REQUIRE_PARAMS(2);
5852     curr_user = GetChannelAccess(cData, user->handle_info);
5853     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5854     if(!curr_user || (curr_user->access != UL_OWNER))
5855     {
5856         struct userData *owner = NULL;
5857         for(curr_user = channel->channel_info->users;
5858             curr_user;
5859             curr_user = curr_user->next)
5860         {
5861             if(curr_user->access != UL_OWNER)
5862                 continue;
5863             if(owner)
5864             {
5865                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5866                 return 0;
5867             }
5868             owner = curr_user;
5869         }
5870         curr_user = owner;
5871     }
5872     else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5873     {
5874         char delay[INTERVALLEN];
5875         intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5876         reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5877         return 0;
5878     }
5879     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5880         return 0;
5881     if(new_owner_hi == user->handle_info)
5882     {
5883         reply("CSMSG_NO_TRANSFER_SELF");
5884         return 0;
5885     }
5886     new_owner = GetChannelAccess(cData, new_owner_hi);
5887     if(!new_owner)
5888     {
5889         if(force)
5890         {
5891             new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5892         }
5893         else
5894         {
5895             reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5896             return 0;
5897         }
5898     }
5899     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5900     {
5901         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5902         return 0;
5903     }
5904     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5905         if(!IsHelping(user))
5906             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5907         else
5908             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5909         return 0;
5910     }
5911     invoker = GetChannelUser(cData, user->handle_info);
5912     if(invoker->access <= UL_OWNER)
5913     {
5914         confirm = make_confirmation_string(curr_user);
5915         if((argc < 3) || strcmp(argv[2], confirm))
5916         {
5917             reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5918             return 0;
5919         }
5920     }
5921     if(new_owner->access >= UL_COOWNER)
5922         co_access = new_owner->access;
5923     else
5924         co_access = UL_COOWNER;
5925     new_owner->access = UL_OWNER;
5926     if(curr_user)
5927         curr_user->access = co_access;
5928     cData->ownerTransfer = now;
5929     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5930     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5931     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5932     return 1;
5933 }
5934
5935 static CHANSERV_FUNC(cmd_suspend)
5936 {
5937     struct handle_info *hi;
5938     struct userData *self, *real_self, *target;
5939     unsigned int override = 0;
5940
5941     REQUIRE_PARAMS(2);
5942     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5943     self = GetChannelUser(channel->channel_info, user->handle_info);
5944     real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5945     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5946     {
5947         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5948         return 0;
5949     }
5950     if(target->access >= self->access)
5951     {
5952         reply("MSG_USER_OUTRANKED", hi->handle);
5953         return 0;
5954     }
5955     if(target->flags & USER_SUSPENDED)
5956     {
5957         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5958         return 0;
5959     }
5960     if(target->present)
5961     {
5962         target->present = 0;
5963         target->seen = now;
5964     }
5965     if(!real_self || target->access >= real_self->access)
5966         override = CMD_LOG_OVERRIDE;
5967     target->flags |= USER_SUSPENDED;
5968     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5969     return 1 | override;
5970 }
5971
5972 static CHANSERV_FUNC(cmd_unsuspend)
5973 {
5974     struct handle_info *hi;
5975     struct userData *self, *real_self, *target;
5976     unsigned int override = 0;
5977
5978     REQUIRE_PARAMS(2);
5979     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5980     self = GetChannelUser(channel->channel_info, user->handle_info);
5981     real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5982     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5983     {
5984         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5985         return 0;
5986     }
5987     if(target->access >= self->access)
5988     {
5989         reply("MSG_USER_OUTRANKED", hi->handle);
5990         return 0;
5991     }
5992     if(!(target->flags & USER_SUSPENDED))
5993     {
5994         reply("CSMSG_NOT_SUSPENDED", hi->handle);
5995         return 0;
5996     }
5997     if(!real_self || target->access >= real_self->access)
5998         override = CMD_LOG_OVERRIDE;
5999     target->flags &= ~USER_SUSPENDED;
6000     scan_user_presence(target, NULL);
6001     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6002     return 1 | override;
6003 }
6004
6005 static MODCMD_FUNC(cmd_deleteme)
6006 {
6007     struct handle_info *hi;
6008     struct userData *target;
6009     const char *confirm_string;
6010     unsigned short access;
6011     char *channel_name;
6012
6013     hi = user->handle_info;
6014     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6015     {
6016         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6017         return 0;
6018     }
6019     if(target->access == UL_OWNER)
6020     {
6021         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6022         return 0;
6023     }
6024     confirm_string = make_confirmation_string(target);
6025     if((argc < 2) || strcmp(argv[1], confirm_string))
6026     {
6027         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6028         return 0;
6029     }
6030     access = target->access;
6031     channel_name = strdup(channel->name);
6032     del_channel_user(target, 1);
6033     reply("CSMSG_DELETED_YOU", access, channel_name);
6034     free(channel_name);
6035     return 1;
6036 }
6037
6038 static void
6039 chanserv_refresh_topics(UNUSED_ARG(void *data))
6040 {
6041     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6042     struct chanData *cData;
6043     char opt;
6044
6045     for(cData = channelList; cData; cData = cData->next)
6046     {
6047         if(IsSuspended(cData))
6048             continue;
6049         opt = cData->chOpts[chTopicRefresh];
6050         if(opt == 'n')
6051             continue;
6052         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6053             continue;
6054         if(cData->topic)
6055             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6056         cData->last_refresh = refresh_num;
6057     }
6058     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6059 }
6060
6061 static CHANSERV_FUNC(cmd_unf)
6062 {
6063     if(channel)
6064     {
6065         char response[MAXLEN];
6066         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6067         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6068         irc_privmsg(cmd->parent->bot, channel->name, response);
6069     }
6070     else
6071         reply("CSMSG_UNF_RESPONSE");
6072     return 1;
6073 }
6074
6075 static CHANSERV_FUNC(cmd_ping)
6076 {
6077     if(channel)
6078     {
6079         char response[MAXLEN];
6080         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6081         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6082         irc_privmsg(cmd->parent->bot, channel->name, response);
6083     }
6084     else
6085         reply("CSMSG_PING_RESPONSE");
6086     return 1;
6087 }
6088
6089 static CHANSERV_FUNC(cmd_wut)
6090 {
6091     if(channel)
6092     {
6093         char response[MAXLEN];
6094         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6095         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6096         irc_privmsg(cmd->parent->bot, channel->name, response);
6097     }
6098     else
6099         reply("CSMSG_WUT_RESPONSE");
6100     return 1;
6101 }
6102
6103 static CHANSERV_FUNC(cmd_8ball)
6104 {
6105     unsigned int i, j, accum;
6106     const char *resp;
6107
6108     REQUIRE_PARAMS(2);
6109     accum = 0;
6110     for(i=1; i<argc; i++)
6111         for(j=0; argv[i][j]; j++)
6112             accum = (accum << 5) - accum + toupper(argv[i][j]);
6113     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6114     if(channel)
6115     {
6116         char response[MAXLEN];
6117         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6118         irc_privmsg(cmd->parent->bot, channel->name, response);
6119     }
6120     else
6121         send_message_type(4, user, cmd->parent->bot, "%s", resp);
6122     return 1;
6123 }
6124
6125 static CHANSERV_FUNC(cmd_d)
6126 {
6127     unsigned long sides, count, modifier, ii, total;
6128     char response[MAXLEN], *sep;
6129     const char *fmt;
6130
6131     REQUIRE_PARAMS(2);
6132     if((count = strtoul(argv[1], &sep, 10)) < 1)
6133         goto no_dice;
6134     if(sep[0] == 0)
6135     {
6136         if(count == 1)
6137             goto no_dice;
6138         sides = count;
6139         count = 1;
6140         modifier = 0;
6141     }
6142     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6143             && (sides = strtoul(sep+1, &sep, 10)) > 1)
6144     {
6145         if(sep[0] == 0)
6146             modifier = 0;
6147         else if((sep[0] == '-') && isdigit(sep[1]))
6148             modifier = strtoul(sep, NULL, 10);
6149         else if((sep[0] == '+') && isdigit(sep[1]))
6150             modifier = strtoul(sep+1, NULL, 10);
6151         else
6152             goto no_dice;
6153     }
6154     else
6155     {
6156       no_dice:
6157         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6158         return 0;
6159     }
6160     if(count > 10)
6161     {
6162         reply("CSMSG_BAD_DICE_COUNT", count, 10);
6163         return 0;
6164     }
6165     for(total = ii = 0; ii < count; ++ii)
6166         total += (rand() % sides) + 1;
6167     total += modifier;
6168
6169     if((count > 1) || modifier)
6170     {
6171         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6172         sprintf(response, fmt, total, count, sides, modifier);
6173     }
6174     else
6175     {
6176         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6177         sprintf(response, fmt, total, sides);
6178     }
6179     if(channel)
6180         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6181     else
6182         send_message_type(4, user, cmd->parent->bot, "%s", response);
6183     return 1;
6184 }
6185
6186 static CHANSERV_FUNC(cmd_huggle)
6187 {
6188     /* CTCP must be via PRIVMSG, never notice */
6189     if(channel)
6190         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6191     else
6192         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6193     return 1;
6194 }
6195
6196 static void
6197 chanserv_adjust_limit(void *data)
6198 {
6199     struct mod_chanmode change;
6200     struct chanData *cData = data;
6201     struct chanNode *channel = cData->channel;
6202     unsigned int limit;
6203
6204     if(IsSuspended(cData))
6205         return;
6206
6207     cData->limitAdjusted = now;
6208     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6209     if(cData->modes.modes_set & MODE_LIMIT)
6210     {
6211         if(limit > cData->modes.new_limit)
6212             limit = cData->modes.new_limit;
6213         else if(limit == cData->modes.new_limit)
6214             return;
6215     }
6216
6217     mod_chanmode_init(&change);
6218     change.modes_set = MODE_LIMIT;
6219     change.new_limit = limit;
6220     mod_chanmode_announce(chanserv, channel, &change);
6221 }
6222
6223 static void
6224 handle_new_channel(struct chanNode *channel)
6225 {
6226     struct chanData *cData;
6227
6228     if(!(cData = channel->channel_info))
6229         return;
6230
6231     if(cData->modes.modes_set || cData->modes.modes_clear)
6232         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6233
6234     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6235         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6236 }
6237
6238 /* Welcome to my worst nightmare. Warning: Read (or modify)
6239    the code below at your own risk. */
6240 static int
6241 handle_join(struct modeNode *mNode)
6242 {
6243     struct mod_chanmode change;
6244     struct userNode *user = mNode->user;
6245     struct chanNode *channel = mNode->channel;
6246     struct chanData *cData;
6247     struct userData *uData = NULL;
6248     struct banData *bData;
6249     struct handle_info *handle;
6250     unsigned int modes = 0, info = 0;
6251     char *greeting;
6252
6253     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6254         return 0;
6255
6256     cData = channel->channel_info;
6257     if(channel->members.used > cData->max)
6258         cData->max = channel->members.used;
6259
6260     /* Check for bans.  If they're joining through a ban, one of two
6261      * cases applies:
6262      *   1: Join during a netburst, by riding the break.  Kick them
6263      *      unless they have ops or voice in the channel.
6264      *   2: They're allowed to join through the ban (an invite in
6265      *   ircu2.10, or a +e on Hybrid, or something).
6266      * If they're not joining through a ban, and the banlist is not
6267      * full, see if they're on the banlist for the channel.  If so,
6268      * kickban them.
6269      */
6270     if(user->uplink->burst && !mNode->modes)
6271     {
6272         unsigned int ii;
6273         for(ii = 0; ii < channel->banlist.used; ii++)
6274         {
6275             if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6276             {
6277                 /* Riding a netburst.  Naughty. */
6278                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6279                 return 1;
6280             }
6281         }
6282     }
6283
6284     mod_chanmode_init(&change);
6285     change.argc = 1;
6286     if(channel->banlist.used < MAXBANS)
6287     {
6288         /* Not joining through a ban. */
6289         for(bData = cData->bans;
6290             bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6291             bData = bData->next);
6292
6293         if(bData)
6294         {
6295             char kick_reason[MAXLEN];
6296             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6297
6298             bData->triggered = now;
6299             if(bData != cData->bans)
6300             {
6301                 /* Shuffle the ban to the head of the list. */
6302                 if(bData->next)
6303                     bData->next->prev = bData->prev;
6304                 if(bData->prev)
6305                     bData->prev->next = bData->next;
6306
6307                 bData->prev = NULL;
6308                 bData->next = cData->bans;
6309
6310                 if(cData->bans)
6311                     cData->bans->prev = bData;
6312                 cData->bans = bData;
6313             }
6314
6315             change.args[0].mode = MODE_BAN;
6316             change.args[0].u.hostmask = bData->mask;
6317             mod_chanmode_announce(chanserv, channel, &change);
6318             KickChannelUser(user, channel, chanserv, kick_reason);
6319             return 1;
6320         }
6321     }
6322
6323     /* ChanServ will not modify the limits in join-flooded channels.
6324        It will also skip DynLimit processing when the user (or srvx)
6325        is bursting in, because there are likely more incoming. */
6326     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6327        && !user->uplink->burst
6328        && !channel->join_flooded
6329        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6330     {
6331         /* The user count has begun "bumping" into the channel limit,
6332            so set a timer to raise the limit a bit. Any previous
6333            timers are removed so three incoming users within the delay
6334            results in one limit change, not three. */
6335
6336         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6337         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6338     }
6339
6340     if(channel->join_flooded)
6341     {
6342         /* don't automatically give ops or voice during a join flood */
6343     }
6344     else if(cData->lvlOpts[lvlGiveOps] == 0)
6345         modes |= MODE_CHANOP;
6346     else if(cData->lvlOpts[lvlGiveVoice] == 0)
6347         modes |= MODE_VOICE;
6348
6349     greeting = cData->greeting;
6350     if(user->handle_info)
6351     {
6352         handle = user->handle_info;
6353
6354         if(IsHelper(user) && !IsHelping(user))
6355         {
6356             unsigned int ii;
6357             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6358             {
6359                 if(channel == chanserv_conf.support_channels.list[ii])
6360                 {
6361                     HANDLE_SET_FLAG(user->handle_info, HELPING);
6362                     break;
6363                 }
6364             }
6365         }
6366
6367         uData = GetTrueChannelAccess(cData, handle);
6368         if(uData && !IsUserSuspended(uData))
6369         {
6370             /* Ops and above were handled by the above case. */
6371             if(IsUserAutoOp(uData))
6372             {
6373                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6374                     modes |= MODE_CHANOP;
6375                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6376                     modes |= MODE_VOICE;
6377             }
6378             if(uData->access >= UL_PRESENT)
6379                 cData->visited = now;
6380             if(cData->user_greeting)
6381                 greeting = cData->user_greeting;
6382             if(uData->info
6383                && (uData->access >= cData->lvlOpts[lvlUserInfo])
6384                && ((now - uData->seen) >= chanserv_conf.info_delay)
6385                && !uData->present)
6386                 info = 1;
6387             uData->seen = now;
6388             uData->present = 1;
6389         }
6390     }
6391
6392     /* If user joining normally (not during burst), apply op or voice,
6393      * and send greeting/userinfo as appropriate.
6394      */
6395     if(!user->uplink->burst)
6396     {
6397         if(modes)
6398         {
6399             if(modes & MODE_CHANOP)
6400                 modes &= ~MODE_VOICE;
6401             change.args[0].mode = modes;
6402             change.args[0].u.member = mNode;
6403             mod_chanmode_announce(chanserv, channel, &change);
6404         }
6405         if(greeting)
6406             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6407         if(uData && info)
6408             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6409     }
6410     return 0;
6411 }
6412
6413 static void
6414 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6415 {
6416     struct mod_chanmode change;
6417     struct userData *channel;
6418     unsigned int ii, jj;
6419
6420     if(!user->handle_info)
6421         return;
6422
6423     mod_chanmode_init(&change);
6424     change.argc = 1;
6425     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6426     {
6427         struct chanNode *cn;
6428         struct modeNode *mn;
6429         if(IsUserSuspended(channel)
6430            || IsSuspended(channel->channel)
6431            || !(cn = channel->channel->channel))
6432             continue;
6433
6434         mn = GetUserMode(cn, user);
6435         if(!mn)
6436         {
6437             if(!IsUserSuspended(channel)
6438                && IsUserAutoInvite(channel)
6439                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6440                && !self->burst
6441                && !user->uplink->burst)
6442                 irc_invite(chanserv, user, cn);
6443             continue;
6444         }
6445
6446         if(channel->access >= UL_PRESENT)
6447             channel->channel->visited = now;
6448
6449         if(IsUserAutoOp(channel))
6450         {
6451             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6452                 change.args[0].mode = MODE_CHANOP;
6453             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6454                 change.args[0].mode = MODE_VOICE;
6455             else
6456                 change.args[0].mode = 0;
6457             change.args[0].u.member = mn;
6458             if(change.args[0].mode)
6459                 mod_chanmode_announce(chanserv, cn, &change);
6460         }
6461
6462         channel->seen = now;
6463         channel->present = 1;
6464     }
6465
6466     for(ii = 0; ii < user->channels.used; ++ii)
6467     {
6468         struct chanNode *channel = user->channels.list[ii]->channel;
6469         struct banData *ban;
6470
6471         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6472            || !channel->channel_info
6473            || IsSuspended(channel->channel_info))
6474             continue;
6475         for(jj = 0; jj < channel->banlist.used; ++jj)
6476             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6477                 break;
6478         if(jj < channel->banlist.used)
6479             continue;
6480         for(ban = channel->channel_info->bans; ban; ban = ban->next)
6481         {
6482             char kick_reason[MAXLEN];
6483             if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6484                 continue;
6485             change.args[0].mode = MODE_BAN;
6486             change.args[0].u.hostmask = ban->mask;
6487             mod_chanmode_announce(chanserv, channel, &change);
6488             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6489             KickChannelUser(user, channel, chanserv, kick_reason);
6490             ban->triggered = now;
6491             break;
6492         }
6493     }
6494
6495     if(IsSupportHelper(user))
6496     {
6497         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6498         {
6499             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6500             {
6501                 HANDLE_SET_FLAG(user->handle_info, HELPING);
6502                 break;
6503             }
6504         }
6505     }
6506 }
6507
6508 static void
6509 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6510 {
6511     struct chanData *cData;
6512     struct userData *uData;
6513
6514     cData = mn->channel->channel_info;
6515     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6516         return;
6517
6518     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6519     {
6520         /* Allow for a bit of padding so that the limit doesn't
6521            track the user count exactly, which could get annoying. */
6522         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6523         {
6524             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6525             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6526         }
6527     }
6528
6529     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6530     {
6531         scan_user_presence(uData, mn->user);
6532         uData->seen = now;
6533         if (uData->access >= UL_PRESENT)
6534             cData->visited = now;
6535     }
6536
6537     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6538     {
6539         unsigned int ii, jj;
6540         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6541         {
6542             for(jj = 0; jj < mn->user->channels.used; ++jj)
6543                 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6544                     break;
6545             if(jj < mn->user->channels.used)
6546                 break;
6547         }
6548         if(ii == chanserv_conf.support_channels.used)
6549             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6550     }
6551 }
6552
6553 static void
6554 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6555 {
6556     struct userData *uData;
6557
6558     if(!channel->channel_info || !kicker || IsService(kicker)
6559        || (kicker == victim) || IsSuspended(channel->channel_info)
6560        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6561         return;
6562
6563     if(protect_user(victim, kicker, channel->channel_info))
6564     {
6565         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6566         KickChannelUser(kicker, channel, chanserv, reason);
6567     }
6568
6569     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6570         uData->seen = now;
6571 }
6572
6573 static int
6574 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6575 {
6576     struct chanData *cData;
6577
6578     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6579         return 0;
6580
6581     cData = channel->channel_info;
6582     if(bad_topic(channel, user, channel->topic))
6583     {
6584         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6585         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6586             SetChannelTopic(channel, chanserv, old_topic, 1);
6587         else if(cData->topic)
6588             SetChannelTopic(channel, chanserv, cData->topic, 1);
6589         return 1;
6590     }
6591     /* With topicsnarf, grab the topic and save it as the default topic. */
6592     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6593     {
6594         free(cData->topic);
6595         cData->topic = strdup(channel->topic);
6596     }
6597     return 0;
6598 }
6599
6600 static void
6601 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6602 {
6603     struct mod_chanmode *bounce = NULL;
6604     unsigned int bnc, ii;
6605     char deopped = 0;
6606
6607     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6608         return;
6609
6610     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6611        && mode_lock_violated(&channel->channel_info->modes, change))
6612     {
6613         char correct[MAXLEN];
6614         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6615         mod_chanmode_format(&channel->channel_info->modes, correct);
6616         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6617     }
6618     for(ii = bnc = 0; ii < change->argc; ++ii)
6619     {
6620         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6621         {
6622             const struct userNode *victim = change->args[ii].u.member->user;
6623             if(!protect_user(victim, user, channel->channel_info))
6624                 continue;
6625             if(!bounce)
6626                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6627             if(!deopped)
6628             {
6629                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6630                 bounce->args[bnc].u.member = GetUserMode(channel, user);
6631                 if(bounce->args[bnc].u.member)
6632                     bnc++;
6633                 deopped = 1;
6634             }
6635             bounce->args[bnc].mode = MODE_CHANOP;
6636             bounce->args[bnc].u.member = change->args[ii].u.member;
6637             bnc++;
6638             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6639         }
6640         else if(change->args[ii].mode & MODE_CHANOP)
6641         {
6642             const struct userNode *victim = change->args[ii].u.member->user;
6643             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6644                 continue;
6645             if(!bounce)
6646                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6647             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6648             bounce->args[bnc].u.member = change->args[ii].u.member;
6649             bnc++;
6650         }
6651         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6652         {
6653             const char *ban = change->args[ii].u.hostmask;
6654             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6655                 continue;
6656             if(!bounce)
6657                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6658             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6659             bounce->args[bnc].u.hostmask = strdup(ban);
6660             bnc++;
6661             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6662         }
6663     }
6664     if(bounce)
6665     {
6666         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6667             mod_chanmode_announce(chanserv, channel, bounce);
6668         for(ii = 0; ii < change->argc; ++ii)
6669             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6670                 free((char*)bounce->args[ii].u.hostmask);
6671         mod_chanmode_free(bounce);
6672     }
6673 }
6674
6675 static void
6676 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6677 {
6678     struct chanNode *channel;
6679     struct banData *bData;
6680     struct mod_chanmode change;
6681     unsigned int ii, jj;
6682     char kick_reason[MAXLEN];
6683
6684     mod_chanmode_init(&change);
6685     change.argc = 1;
6686     change.args[0].mode = MODE_BAN;
6687     for(ii = 0; ii < user->channels.used; ++ii)
6688     {
6689         channel = user->channels.list[ii]->channel;
6690         /* Need not check for bans if they're opped or voiced. */
6691         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6692             continue;
6693         /* Need not check for bans unless channel registration is active. */
6694         if(!channel->channel_info || IsSuspended(channel->channel_info))
6695             continue;
6696         /* Look for a matching ban already on the channel. */
6697         for(jj = 0; jj < channel->banlist.used; ++jj)
6698             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6699                 break;
6700         /* Need not act if we found one. */
6701         if(jj < channel->banlist.used)
6702             continue;
6703         /* Look for a matching ban in this channel. */
6704         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6705         {
6706             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6707                 continue;
6708             change.args[0].u.hostmask = bData->mask;
6709             mod_chanmode_announce(chanserv, channel, &change);
6710             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6711             KickChannelUser(user, channel, chanserv, kick_reason);
6712             bData->triggered = now;
6713             break; /* we don't need to check any more bans in the channel */
6714         }
6715     }
6716 }
6717
6718 static void handle_rename(struct handle_info *handle, const char *old_handle)
6719 {
6720     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6721
6722     if(dnr)
6723     {
6724         dict_remove2(handle_dnrs, old_handle, 1);
6725         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6726         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6727     }
6728 }
6729
6730 static void
6731 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6732 {
6733     struct userNode *h_user;
6734
6735     if(handle->channels)
6736     {
6737         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6738             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6739
6740         while(handle->channels)
6741             del_channel_user(handle->channels, 1);
6742     }
6743 }
6744
6745 static void
6746 handle_server_link(UNUSED_ARG(struct server *server))
6747 {
6748     struct chanData *cData;
6749
6750     for(cData = channelList; cData; cData = cData->next)
6751     {
6752         if(!IsSuspended(cData))
6753             cData->may_opchan = 1;
6754         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6755            && !cData->channel->join_flooded
6756            && ((cData->channel->limit - cData->channel->members.used)
6757                < chanserv_conf.adjust_threshold))
6758         {
6759             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6760             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6761         }
6762     }
6763 }
6764
6765 static void
6766 chanserv_conf_read(void)
6767 {
6768     dict_t conf_node;
6769     const char *str;
6770     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6771     struct mod_chanmode *change;
6772     struct string_list *strlist;
6773     struct chanNode *chan;
6774     unsigned int ii;
6775
6776     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6777     {
6778         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6779         return;
6780     }
6781     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6782         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6783     chanserv_conf.support_channels.used = 0;
6784     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6785     {
6786         for(ii = 0; ii < strlist->used; ++ii)
6787         {
6788             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6789             if(!str2)
6790                 str2 = "+nt";
6791             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6792             LockChannel(chan);
6793             channelList_append(&chanserv_conf.support_channels, chan);
6794         }
6795     }
6796     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6797     {
6798         const char *str2;
6799         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6800         if(!str2)
6801             str2 = "+nt";
6802         chan = AddChannel(str, now, str2, NULL);
6803         LockChannel(chan);
6804         channelList_append(&chanserv_conf.support_channels, chan);
6805     }
6806     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6807     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6808     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6809     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6810     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6811     chanserv_conf.greeting_length = str ? atoi(str) : 200;
6812     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6813     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6814     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6815     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6816     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6817     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6818     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6819     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6820     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6821     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6822     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6823     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6824     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6825     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6826     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6827     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6828     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6829     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6830     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6831     if(chanserv && str)
6832         NickChange(chanserv, str, 0);
6833     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6834     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6835     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6836     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6837     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6838     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6839     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6840     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6841     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6842     chanserv_conf.max_owned = str ? atoi(str) : 5;
6843     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6844     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6845     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6846     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6847     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6848     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6849     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6850     if(!str)
6851         str = "+nt";
6852     safestrncpy(mode_line, str, sizeof(mode_line));
6853     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6854     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6855        && (change->argc < 2))
6856     {
6857         chanserv_conf.default_modes = *change;
6858         mod_chanmode_free(change);
6859     }
6860     free_string_list(chanserv_conf.set_shows);
6861     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6862     if(strlist)
6863         strlist = string_list_copy(strlist);
6864     else
6865     {
6866         static const char *list[] = {
6867             /* free form text */
6868             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6869             /* options based on user level */
6870             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6871             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6872             /* multiple choice options */
6873             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6874             /* binary options */
6875             "DynLimit", "NoDelete",
6876             /* delimiter */
6877             NULL
6878         };
6879         unsigned int ii;
6880         strlist = alloc_string_list(ArrayLength(list)-1);
6881         for(ii=0; list[ii]; ii++)
6882             string_list_append(strlist, strdup(list[ii]));
6883     }
6884     chanserv_conf.set_shows = strlist;
6885     /* We don't look things up now, in case the list refers to options
6886      * defined by modules initialized after this point.  Just mark the
6887      * function list as invalid, so it will be initialized.
6888      */
6889     set_shows_list.used = 0;
6890     free_string_list(chanserv_conf.eightball);
6891     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6892     if(strlist)
6893     {
6894         strlist = string_list_copy(strlist);
6895     }
6896     else
6897     {
6898         strlist = alloc_string_list(4);
6899         string_list_append(strlist, strdup("Yes."));
6900         string_list_append(strlist, strdup("No."));
6901         string_list_append(strlist, strdup("Maybe so."));
6902     }
6903     chanserv_conf.eightball = strlist;
6904     free_string_list(chanserv_conf.old_ban_names);
6905     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6906     if(strlist)
6907         strlist = string_list_copy(strlist);
6908     else
6909         strlist = alloc_string_list(2);
6910     chanserv_conf.old_ban_names = strlist;
6911     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6912     off_channel = str ? atoi(str) : 0;
6913 }
6914
6915 static void
6916 chanserv_note_type_read(const char *key, struct record_data *rd)
6917 {
6918     dict_t obj;
6919     struct note_type *ntype;
6920     const char *str;
6921
6922     if(!(obj = GET_RECORD_OBJECT(rd)))
6923     {
6924         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6925         return;
6926     }
6927     if(!(ntype = chanserv_create_note_type(key)))
6928     {
6929         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6930         return;
6931     }
6932
6933     /* Figure out set access */
6934     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6935     {
6936         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6937         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6938     }
6939     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6940     {
6941         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6942         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6943     }
6944     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6945     {
6946         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6947     }
6948     else
6949     {
6950         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6951         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6952         ntype->set_access.min_opserv = 0;
6953     }
6954
6955     /* Figure out visibility */
6956     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6957         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6958     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6959         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6960     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6961         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6962     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6963         ntype->visible_type = NOTE_VIS_ALL;
6964     else
6965         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6966
6967     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6968     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6969 }
6970
6971 static void
6972 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6973 {
6974     struct handle_info *handle;
6975     struct userData *uData;
6976     char *seen, *inf, *flags;
6977     time_t last_seen;
6978     unsigned short access;
6979
6980     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6981     {
6982         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6983         return;
6984     }
6985
6986     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6987     if(access > UL_OWNER)
6988     {
6989         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6990         return;
6991     }
6992
6993     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6994     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6995     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6996     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6997     handle = get_handle_info(key);
6998     if(!handle)
6999     {
7000         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7001         return;
7002     }
7003
7004     uData = add_channel_user(chan, handle, access, last_seen, inf);
7005     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7006 }
7007
7008 static void
7009 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7010 {
7011     struct banData *bData;
7012     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7013     time_t set_time, triggered_time, expires_time;
7014
7015     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7016     {
7017         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7018         return;
7019     }
7020
7021     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7022     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7023     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7024     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7025     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7026     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7027     if (!reason || !owner)
7028         return;
7029
7030     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7031     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7032     if(s_expires)
7033         expires_time = (time_t)strtoul(s_expires, NULL, 0);
7034     else if(s_duration)
7035         expires_time = set_time + atoi(s_duration);
7036     else
7037         expires_time = 0;
7038
7039     if(!reason || (expires_time && (expires_time < now)))
7040         return;
7041
7042     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7043 }
7044
7045 static struct suspended *
7046 chanserv_read_suspended(dict_t obj)
7047 {
7048     struct suspended *suspended = calloc(1, sizeof(*suspended));
7049     char *str;
7050     dict_t previous;
7051
7052     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7053     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7054     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7055     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7056     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7057     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7058     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7059     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7060     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7061     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7062     return suspended;
7063 }
7064
7065 static int
7066 chanserv_channel_read(const char *key, struct record_data *hir)
7067 {
7068     struct suspended *suspended;
7069     struct mod_chanmode *modes;
7070     struct chanNode *cNode;
7071     struct chanData *cData;
7072     struct dict *channel, *obj;
7073     char *str, *argv[10];
7074     dict_iterator_t it;
7075     unsigned int argc;
7076
7077     channel = hir->d.object;
7078
7079     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7080     if(!str)
7081         str = "<unknown>";
7082     cNode = AddChannel(key, now, NULL, NULL);
7083     if(!cNode)
7084     {
7085         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7086         return 0;
7087     }
7088     cData = register_channel(cNode, str);
7089     if(!cData)
7090     {
7091         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7092         return 0;
7093     }
7094
7095     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7096     {
7097         enum levelOption lvlOpt;
7098         enum charOption chOpt;
7099
7100         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7101             cData->flags = atoi(str);
7102
7103         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7104         {
7105             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7106             if(str)
7107                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7108             else if(levelOptions[lvlOpt].old_flag)
7109             {
7110                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7111                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7112                 else
7113                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7114             }
7115         }
7116
7117         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7118         {
7119             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7120                 continue;
7121             cData->chOpts[chOpt] = str[0];
7122         }
7123     }
7124     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7125     {
7126         enum levelOption lvlOpt;
7127         enum charOption chOpt;
7128         unsigned int count;
7129
7130         cData->flags = base64toint(str, 5);
7131         count = strlen(str += 5);
7132         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7133         {
7134             unsigned short lvl;
7135             if(levelOptions[lvlOpt].old_flag)
7136             {
7137                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7138                     lvl = levelOptions[lvlOpt].flag_value;
7139                 else
7140                     lvl = levelOptions[lvlOpt].default_value;
7141             }
7142             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7143             {
7144             case 'c': lvl = UL_COOWNER; break;
7145             case 'm': lvl = UL_MASTER; break;
7146             case 'n': lvl = UL_OWNER+1; break;
7147             case 'o': lvl = UL_OP; break;
7148             case 'p': lvl = UL_PEON; break;
7149             case 'w': lvl = UL_OWNER; break;
7150             default: lvl = 0; break;
7151             }
7152             cData->lvlOpts[lvlOpt] = lvl;
7153         }
7154         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7155             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7156     }
7157    
7158     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7159     {
7160         suspended = chanserv_read_suspended(obj);
7161         cData->suspended = suspended;
7162         suspended->cData = cData;
7163         /* We could use suspended->expires and suspended->revoked to
7164          * set the CHANNEL_SUSPENDED flag, but we don't. */
7165     }
7166     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7167     {
7168         suspended = calloc(1, sizeof(*suspended));
7169         suspended->issued = 0;
7170         suspended->revoked = 0;
7171         suspended->suspender = strdup(str);
7172         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7173         suspended->expires = str ? atoi(str) : 0;
7174         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7175         suspended->reason = strdup(str ? str : "No reason");
7176         suspended->previous = NULL;
7177         cData->suspended = suspended;
7178         suspended->cData = cData;
7179     }
7180     else
7181     {
7182         cData->flags &= ~CHANNEL_SUSPENDED;
7183         suspended = NULL; /* to squelch a warning */
7184     }
7185
7186     if(IsSuspended(cData)) {
7187         if(suspended->expires > now)
7188             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7189         else if(suspended->expires)
7190             cData->flags &= ~CHANNEL_SUSPENDED;
7191     }
7192
7193     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7194         struct mod_chanmode change;
7195         mod_chanmode_init(&change);
7196         change.argc = 1;
7197         change.args[0].mode = MODE_CHANOP;
7198         change.args[0].u.member = AddChannelUser(chanserv, cNode);
7199         mod_chanmode_announce(chanserv, cNode, &change);
7200     }
7201
7202     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7203     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7204     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7205     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7206     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7207     cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7208     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7209     cData->max = str ? atoi(str) : 0;
7210     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7211     cData->greeting = str ? strdup(str) : NULL;
7212     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7213     cData->user_greeting = str ? strdup(str) : NULL;
7214     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7215     cData->topic_mask = str ? strdup(str) : NULL;
7216     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7217     cData->topic = str ? strdup(str) : NULL;
7218
7219     if(!IsSuspended(cData)
7220        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7221        && (argc = split_line(str, 0, ArrayLength(argv), argv))
7222        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7223         cData->modes = *modes;
7224         if(off_channel > 0)
7225           cData->modes.modes_set |= MODE_REGISTERED;
7226         if(cData->modes.argc > 1)
7227             cData->modes.argc = 1;
7228         mod_chanmode_announce(chanserv, cNode, &cData->modes);
7229         mod_chanmode_free(modes);
7230     }
7231
7232     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7233     for(it = dict_first(obj); it; it = iter_next(it))
7234         user_read_helper(iter_key(it), iter_data(it), cData);
7235
7236     if(!cData->users && !IsProtected(cData))
7237     {
7238         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7239         unregister_channel(cData, "has empty user list.");
7240         return 0;
7241     }
7242
7243     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7244     for(it = dict_first(obj); it; it = iter_next(it))
7245         ban_read_helper(iter_key(it), iter_data(it), cData);
7246
7247     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7248     for(it = dict_first(obj); it; it = iter_next(it))
7249     {
7250         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7251         struct record_data *rd = iter_data(it);
7252         const char *note, *setter;
7253
7254         if(rd->type != RECDB_OBJECT)
7255         {
7256             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7257         }
7258         else if(!ntype)
7259         {
7260             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7261         }
7262         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7263         {
7264             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7265         }
7266         else
7267         {
7268             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7269             if(!setter) setter = "<unknown>";
7270             chanserv_add_channel_note(cData, ntype, setter, note);
7271         }
7272     }
7273
7274     return 0;
7275 }
7276
7277 static void
7278 chanserv_dnr_read(const char *key, struct record_data *hir)
7279 {
7280     const char *setter, *reason, *str;
7281     struct do_not_register *dnr;
7282     time_t expiry;
7283
7284     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7285     if(!setter)
7286     {
7287         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7288         return;
7289     }
7290     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7291     if(!reason)
7292     {
7293         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7294         return;
7295     }
7296     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7297     expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7298     if(expiry && expiry <= now)
7299         return;
7300     dnr = chanserv_add_dnr(key, setter, expiry, reason);
7301     if(!dnr)
7302         return;
7303     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7304     if(str)
7305         dnr->set = atoi(str);
7306     else
7307         dnr->set = 0;
7308 }
7309
7310 static int
7311 chanserv_saxdb_read(struct dict *database)
7312 {
7313     struct dict *section;
7314     dict_iterator_t it;
7315
7316     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7317         for(it = dict_first(section); it; it = iter_next(it))
7318             chanserv_note_type_read(iter_key(it), iter_data(it));
7319
7320     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7321         for(it = dict_first(section); it; it = iter_next(it))
7322             chanserv_channel_read(iter_key(it), iter_data(it));
7323
7324     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7325         for(it = dict_first(section); it; it = iter_next(it))
7326             chanserv_dnr_read(iter_key(it), iter_data(it));
7327
7328     return 0;
7329 }
7330
7331 static int
7332 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7333 {
7334     int high_present = 0;
7335     saxdb_start_record(ctx, KEY_USERS, 1);
7336     for(; uData; uData = uData->next)
7337     {
7338         if((uData->access >= UL_PRESENT) && uData->present)
7339             high_present = 1;
7340         saxdb_start_record(ctx, uData->handle->handle, 0);
7341         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7342         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7343         if(uData->flags)
7344             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7345         if(uData->info)
7346             saxdb_write_string(ctx, KEY_INFO, uData->info);
7347         saxdb_end_record(ctx);
7348     }
7349     saxdb_end_record(ctx);
7350     return high_present;
7351 }
7352
7353 static void
7354 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7355 {
7356     if(!bData)
7357         return;
7358     saxdb_start_record(ctx, KEY_BANS, 1);
7359     for(; bData; bData = bData->next)
7360     {
7361         saxdb_start_record(ctx, bData->mask, 0);
7362         saxdb_write_int(ctx, KEY_SET, bData->set);
7363         if(bData->triggered)
7364             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7365         if(bData->expires)
7366             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7367         if(bData->owner[0])
7368             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7369         if(bData->reason)
7370             saxdb_write_string(ctx, KEY_REASON, bData->reason);
7371         saxdb_end_record(ctx);
7372     }
7373     saxdb_end_record(ctx);
7374 }
7375
7376 static void
7377 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7378 {
7379     saxdb_start_record(ctx, name, 0);
7380     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7381     saxdb_write_string(ctx, KEY_REASON, susp->reason);
7382     if(susp->issued)
7383         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7384     if(susp->expires)
7385         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7386     if(susp->revoked)
7387         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7388     if(susp->previous)
7389         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7390     saxdb_end_record(ctx);
7391 }
7392
7393 static void
7394 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7395 {
7396     char buf[MAXLEN];
7397     int high_present;
7398     enum levelOption lvlOpt;
7399     enum charOption chOpt;
7400
7401     saxdb_start_record(ctx, channel->channel->name, 1);
7402
7403     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7404     saxdb_write_int(ctx, KEY_MAX, channel->max);
7405     if(channel->topic)
7406         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7407     if(channel->registrar)
7408         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7409     if(channel->greeting)
7410         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7411     if(channel->user_greeting)
7412         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7413     if(channel->topic_mask)
7414         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7415     if(channel->suspended)
7416         chanserv_write_suspended(ctx, "suspended", channel->suspended);
7417
7418     saxdb_start_record(ctx, KEY_OPTIONS, 0);
7419     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7420     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7421         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7422     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7423     {
7424         buf[0] = channel->chOpts[chOpt];
7425         buf[1] = '\0';
7426         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7427     }
7428     saxdb_end_record(ctx);
7429
7430     if(channel->modes.modes_set || channel->modes.modes_clear)
7431     {
7432         mod_chanmode_format(&channel->modes, buf);
7433         saxdb_write_string(ctx, KEY_MODES, buf);
7434     }
7435
7436     high_present = chanserv_write_users(ctx, channel->users);
7437     chanserv_write_bans(ctx, channel->bans);
7438
7439     if(dict_size(channel->notes))
7440     {
7441         dict_iterator_t it;
7442
7443         saxdb_start_record(ctx, KEY_NOTES, 1);
7444         for(it = dict_first(channel->notes); it; it = iter_next(it))
7445         {
7446             struct note *note = iter_data(it);
7447             saxdb_start_record(ctx, iter_key(it), 0);
7448             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7449             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7450             saxdb_end_record(ctx);
7451         }
7452         saxdb_end_record(ctx);
7453     }
7454
7455     if(channel->ownerTransfer)
7456         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7457     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7458     saxdb_end_record(ctx);
7459 }
7460
7461 static void
7462 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7463 {
7464     const char *str;
7465
7466     saxdb_start_record(ctx, ntype->name, 0);
7467     switch(ntype->set_access_type)
7468     {
7469     case NOTE_SET_CHANNEL_ACCESS:
7470         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7471         break;
7472     case NOTE_SET_CHANNEL_SETTER:
7473         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7474         break;
7475     case NOTE_SET_PRIVILEGED: default:
7476         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7477         break;
7478     }
7479     switch(ntype->visible_type)
7480     {
7481     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7482     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7483     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7484     }
7485     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7486     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7487     saxdb_end_record(ctx);
7488 }
7489
7490 static void
7491 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7492 {
7493     struct do_not_register *dnr;
7494     dict_iterator_t it, next;
7495
7496     for(it = dict_first(dnrs); it; it = next)
7497     {
7498         next = iter_next(it);
7499         dnr = iter_data(it);
7500         if(dnr->expires && dnr->expires <= now)
7501         {
7502             dict_remove(dnrs, iter_key(it));
7503             continue;
7504         }
7505         saxdb_start_record(ctx, dnr->chan_name, 0);
7506         if(dnr->set)
7507             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7508         if(dnr->expires)
7509             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7510         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7511         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7512         saxdb_end_record(ctx);
7513     }
7514 }
7515
7516 static int
7517 chanserv_saxdb_write(struct saxdb_context *ctx)
7518 {
7519     dict_iterator_t it;
7520     struct chanData *channel;
7521
7522     /* Notes */
7523     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7524     for(it = dict_first(note_types); it; it = iter_next(it))
7525         chanserv_write_note_type(ctx, iter_data(it));
7526     saxdb_end_record(ctx);
7527
7528     /* DNRs */
7529     saxdb_start_record(ctx, KEY_DNR, 1);
7530     write_dnrs_helper(ctx, handle_dnrs);
7531     write_dnrs_helper(ctx, plain_dnrs);
7532     write_dnrs_helper(ctx, mask_dnrs);
7533     saxdb_end_record(ctx);
7534
7535     /* Channels */
7536     saxdb_start_record(ctx, KEY_CHANNELS, 1);
7537     for(channel = channelList; channel; channel = channel->next)
7538         chanserv_write_channel(ctx, channel);
7539     saxdb_end_record(ctx);
7540
7541     return 0;
7542 }
7543
7544 static void
7545 chanserv_db_cleanup(void) {
7546     unsigned int ii;
7547     unreg_part_func(handle_part);
7548     while(channelList)
7549         unregister_channel(channelList, "terminating.");
7550     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7551         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7552     free(chanserv_conf.support_channels.list);
7553     dict_delete(handle_dnrs);
7554     dict_delete(plain_dnrs);
7555     dict_delete(mask_dnrs);
7556     dict_delete(note_types);
7557     free_string_list(chanserv_conf.eightball);
7558     free_string_list(chanserv_conf.old_ban_names);
7559     free_string_list(chanserv_conf.set_shows);
7560     free(set_shows_list.list);
7561     free(uset_shows_list.list);
7562     while(helperList)
7563     {
7564         struct userData *helper = helperList;
7565         helperList = helperList->next;
7566         free(helper);
7567     }
7568 }
7569
7570 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7571 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7572 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7573
7574 void
7575 init_chanserv(const char *nick)
7576 {
7577     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7578     conf_register_reload(chanserv_conf_read);
7579
7580     if(nick)
7581     {
7582         reg_server_link_func(handle_server_link);
7583         reg_new_channel_func(handle_new_channel);
7584         reg_join_func(handle_join);
7585         reg_part_func(handle_part);
7586         reg_kick_func(handle_kick);
7587         reg_topic_func(handle_topic);
7588         reg_mode_change_func(handle_mode);
7589         reg_nick_change_func(handle_nick_change);
7590         reg_auth_func(handle_auth);
7591     }
7592
7593     reg_handle_rename_func(handle_rename);
7594     reg_unreg_func(handle_unreg);
7595
7596     handle_dnrs = dict_new();
7597     dict_set_free_data(handle_dnrs, free);
7598     plain_dnrs = dict_new();
7599     dict_set_free_data(plain_dnrs, free);
7600     mask_dnrs = dict_new();
7601     dict_set_free_data(mask_dnrs, free);
7602
7603     reg_svccmd_unbind_func(handle_svccmd_unbind);
7604     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7605     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7606     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7607     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7608     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7609     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7610     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7611     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7612     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7613     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7614     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7615     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7616
7617     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7618     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7619
7620     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7621     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7622     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7623     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7624     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7625
7626     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7627     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7628     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7629     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7630     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7631
7632     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7633     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7634     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7635     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7636
7637     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7638     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7639     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7640     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7641     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7642     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7643     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7644     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7645
7646     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7647     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7648     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7649     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7650     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7651     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7652     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7653     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7654     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7655     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7656     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7657     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7658     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7659     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7660
7661     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7662     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7663     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7664     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7665     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7666
7667     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7668     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7669
7670     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7671     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7672     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7673     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7674     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7675     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7676     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7677     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7678     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7679     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7680     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7681
7682     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7683     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7684
7685     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7686     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7687     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7688     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7689
7690     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7691     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7692     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7693     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7694     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7695
7696     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7697     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7698     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7699     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7700     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7701     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7702
7703     /* Channel options */
7704     DEFINE_CHANNEL_OPTION(defaulttopic);
7705     DEFINE_CHANNEL_OPTION(topicmask);
7706     DEFINE_CHANNEL_OPTION(greeting);
7707     DEFINE_CHANNEL_OPTION(usergreeting);
7708     DEFINE_CHANNEL_OPTION(modes);
7709     DEFINE_CHANNEL_OPTION(enfops);
7710     DEFINE_CHANNEL_OPTION(giveops);
7711     DEFINE_CHANNEL_OPTION(protect);
7712     DEFINE_CHANNEL_OPTION(enfmodes);
7713     DEFINE_CHANNEL_OPTION(enftopic);
7714     DEFINE_CHANNEL_OPTION(pubcmd);
7715     DEFINE_CHANNEL_OPTION(givevoice);
7716     DEFINE_CHANNEL_OPTION(userinfo);
7717     DEFINE_CHANNEL_OPTION(dynlimit);
7718     DEFINE_CHANNEL_OPTION(topicsnarf);
7719     DEFINE_CHANNEL_OPTION(nodelete);
7720     DEFINE_CHANNEL_OPTION(toys);
7721     DEFINE_CHANNEL_OPTION(setters);
7722     DEFINE_CHANNEL_OPTION(topicrefresh);
7723     DEFINE_CHANNEL_OPTION(ctcpusers);
7724     DEFINE_CHANNEL_OPTION(ctcpreaction);
7725     DEFINE_CHANNEL_OPTION(inviteme);
7726     if(off_channel > 1)
7727         DEFINE_CHANNEL_OPTION(offchannel);
7728     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7729
7730     /* Alias set topic to set defaulttopic for compatibility. */
7731     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7732
7733     /* User options */
7734     DEFINE_USER_OPTION(noautoop);
7735     DEFINE_USER_OPTION(autoinvite);
7736     DEFINE_USER_OPTION(info);
7737
7738     /* Alias uset autovoice to uset autoop. */
7739     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7740
7741     note_types = dict_new();
7742     dict_set_free_data(note_types, chanserv_deref_note_type);
7743     if(nick)
7744     {
7745         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7746         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7747         service_register(chanserv)->trigger = '!';
7748         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7749     }
7750     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7751
7752     if(chanserv_conf.channel_expire_frequency)
7753         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7754
7755     if(chanserv_conf.dnr_expire_frequency)
7756         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7757
7758     if(chanserv_conf.refresh_period)
7759     {
7760         time_t next_refresh;
7761         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7762         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7763     }
7764
7765     reg_exit_func(chanserv_db_cleanup);
7766     message_register_table(msgtab);
7767 }