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