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