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