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