fixes for the last commits (buildable now)
[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 void
4516 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4517 {
4518     unsigned int combo;
4519     char buf1[INTERVALLEN], buf2[INTERVALLEN];
4520
4521     /* We display things based on two dimensions:
4522      * - Issue time: present or absent
4523      * - Expiration: revoked, expired, expires in future, or indefinite expiration
4524      * (in order of precedence, so something both expired and revoked
4525      * only counts as revoked)
4526      */
4527     combo = (suspended->issued ? 4 : 0)
4528         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4529     switch(combo) {
4530     case 0: /* no issue time, indefinite expiration */
4531         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4532         break;
4533     case 1: /* no issue time, expires in future */
4534         intervalString(buf1, suspended->expires-now, user->handle_info);
4535         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4536         break;
4537     case 2: /* no issue time, expired */
4538         intervalString(buf1, now-suspended->expires, user->handle_info);
4539         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4540         break;
4541     case 3: /* no issue time, revoked */
4542         intervalString(buf1, now-suspended->revoked, user->handle_info);
4543         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4544         break;
4545     case 4: /* issue time set, indefinite expiration */
4546         intervalString(buf1, now-suspended->issued, user->handle_info);
4547         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4548         break;
4549     case 5: /* issue time set, expires in future */
4550         intervalString(buf1, now-suspended->issued, user->handle_info);
4551         intervalString(buf2, suspended->expires-now, user->handle_info);
4552         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4553         break;
4554     case 6: /* issue time set, expired */
4555         intervalString(buf1, now-suspended->issued, user->handle_info);
4556         intervalString(buf2, now-suspended->expires, user->handle_info);
4557         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4558         break;
4559     case 7: /* issue time set, revoked */
4560         intervalString(buf1, now-suspended->issued, user->handle_info);
4561         intervalString(buf2, now-suspended->revoked, user->handle_info);
4562         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4563         break;
4564     default:
4565         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4566         return;
4567     }
4568 }
4569
4570 static CHANSERV_FUNC(cmd_info)
4571 {
4572     char modes[MAXLEN], buffer[INTERVALLEN];
4573     struct userData *uData, *owner;
4574     struct chanData *cData;
4575     struct do_not_register *dnr;
4576     struct note *note;
4577     dict_iterator_t it;
4578     int privileged;
4579
4580     cData = channel->channel_info;
4581     reply("CSMSG_CHANNEL_INFO", channel->name);
4582
4583     uData = GetChannelUser(cData, user->handle_info);
4584     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4585     {
4586         mod_chanmode_format(&cData->modes, modes);
4587         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4588         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4589     }
4590
4591     for(it = dict_first(cData->notes); it; it = iter_next(it))
4592     {
4593         int padding;
4594
4595         note = iter_data(it);
4596         if(!note_type_visible_to_user(cData, note->type, user))
4597             continue;
4598
4599         padding = PADLEN - 1 - strlen(iter_key(it));
4600         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4601     }
4602
4603     if(cData->max_time) {
4604         reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4605     } else {
4606         reply("CSMSG_CHANNEL_MAX", cData->max);
4607     }
4608     for(owner = cData->users; owner; owner = owner->next)
4609         if(owner->access == UL_OWNER)
4610             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4611     reply("CSMSG_CHANNEL_USERS", cData->userCount);
4612     reply("CSMSG_CHANNEL_BANS", cData->banCount);
4613     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4614
4615     privileged = IsStaff(user);
4616     if(privileged)
4617         reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4618     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4619         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4620
4621     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4622         chanserv_show_dnrs(user, cmd, channel->name, NULL);
4623
4624     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4625     {
4626         struct suspended *suspended;
4627         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4628         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4629             show_suspension_info(cmd, user, suspended);
4630     }
4631     else if(IsSuspended(cData))
4632     {
4633         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4634         show_suspension_info(cmd, user, cData->suspended);
4635     }
4636     return 1;
4637 }
4638
4639 static CHANSERV_FUNC(cmd_netinfo)
4640 {
4641     extern unsigned long boot_time;
4642     extern unsigned long burst_length;
4643     char interval[INTERVALLEN];
4644
4645     reply("CSMSG_NETWORK_INFO");
4646     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4647     reply("CSMSG_NETWORK_USERS", dict_size(clients));
4648     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4649     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4650     reply("CSMSG_NETWORK_BANS", banCount);
4651     reply("CSMSG_NETWORK_CHANUSERS", userCount);
4652     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4653     reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4654     return 1;
4655 }
4656
4657 static void
4658 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4659 {
4660     struct helpfile_table table;
4661     unsigned int nn;
4662     struct userNode *user;
4663     char *nick;
4664
4665     table.length = 0;
4666     table.width = 1;
4667     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4668     table.contents = alloca(list->used*sizeof(*table.contents));
4669     for(nn=0; nn<list->used; nn++)
4670     {
4671         user = list->list[nn];
4672         if(user->modes & skip_flags)
4673             continue;
4674         if(IsBot(user))
4675             continue;
4676         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4677         if(IsAway(user))
4678         {
4679             nick = alloca(strlen(user->nick)+3);
4680             sprintf(nick, "(%s)", user->nick);
4681         }
4682         else
4683             nick = user->nick;
4684         table.contents[table.length][0] = nick;
4685         table.length++;
4686     }
4687     table_send(chanserv, to->nick, 0, NULL, table);
4688 }
4689
4690 static CHANSERV_FUNC(cmd_ircops)
4691 {
4692     reply("CSMSG_STAFF_OPERS");
4693     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4694     return 1;
4695 }
4696
4697 static CHANSERV_FUNC(cmd_helpers)
4698 {
4699     reply("CSMSG_STAFF_HELPERS");
4700     send_staff_list(user, &curr_helpers, FLAGS_OPER);
4701     return 1;
4702 }
4703
4704 static CHANSERV_FUNC(cmd_staff)
4705 {
4706     reply("CSMSG_NETWORK_STAFF");
4707     cmd_ircops(CSFUNC_ARGS);
4708     cmd_helpers(CSFUNC_ARGS);
4709     return 1;
4710 }
4711
4712 static CHANSERV_FUNC(cmd_peek)
4713 {
4714     struct modeNode *mn;
4715     char modes[MODELEN];
4716     unsigned int n;
4717     struct helpfile_table table;
4718     int opcount = 0, voicecount = 0, srvcount = 0;
4719
4720     irc_make_chanmode(channel, modes);
4721
4722     reply("CSMSG_PEEK_INFO", channel->name);
4723     reply("CSMSG_PEEK_TOPIC", channel->topic);
4724     reply("CSMSG_PEEK_MODES", modes);
4725
4726     table.length = 0;
4727     table.width = 1;
4728     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4729     table.contents = alloca(channel->members.used*sizeof(*table.contents));
4730     for(n = 0; n < channel->members.used; n++)
4731     {
4732         mn = channel->members.list[n];
4733         if(IsLocal(mn->user))
4734             srvcount++;
4735         else if(mn->modes & MODE_CHANOP)
4736             opcount++;
4737         else if(mn->modes & MODE_VOICE)
4738             voicecount++;
4739
4740         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4741             continue;
4742         table.contents[table.length] = alloca(sizeof(**table.contents));
4743         table.contents[table.length][0] = mn->user->nick;
4744         table.length++;
4745     }
4746
4747     reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4748           (channel->members.used - opcount - voicecount - srvcount));
4749
4750     if(table.length)
4751     {
4752         reply("CSMSG_PEEK_OPS");
4753         table_send(chanserv, user->nick, 0, NULL, table);
4754     }
4755     else
4756         reply("CSMSG_PEEK_NO_OPS");
4757     return 1;
4758 }
4759
4760 static MODCMD_FUNC(cmd_wipeinfo)
4761 {
4762     struct handle_info *victim;
4763     struct userData *ud, *actor, *real_actor;
4764     unsigned int override = 0;
4765
4766     REQUIRE_PARAMS(2);
4767     actor = GetChannelUser(channel->channel_info, user->handle_info);
4768     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4769     if(!(victim = modcmd_get_handle_info(user, argv[1])))
4770         return 0;
4771     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4772     {
4773         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4774         return 0;
4775     }
4776     if((ud->access >= actor->access) && (ud != actor))
4777     {
4778         reply("MSG_USER_OUTRANKED", victim->handle);
4779         return 0;
4780     }
4781     if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4782         override = CMD_LOG_OVERRIDE;
4783     if(ud->info)
4784         free(ud->info);
4785     ud->info = NULL;
4786     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4787     return 1 | override;
4788 }
4789
4790 static CHANSERV_FUNC(cmd_resync)
4791 {
4792     struct mod_chanmode *changes;
4793     struct chanData *cData = channel->channel_info;
4794     unsigned int ii, used;
4795
4796     changes = mod_chanmode_alloc(channel->members.used * 2);
4797     for(ii = used = 0; ii < channel->members.used; ++ii)
4798     {
4799         struct modeNode *mn = channel->members.list[ii];
4800         struct userData *uData;
4801
4802         if(IsService(mn->user))
4803             continue;
4804
4805         uData = GetChannelAccess(cData, mn->user->handle_info);
4806         if(!cData->lvlOpts[lvlGiveOps]
4807            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4808         {
4809             if(!(mn->modes & MODE_CHANOP))
4810             {
4811                 changes->args[used].mode = MODE_CHANOP;
4812                 changes->args[used++].u.member = mn;
4813             }
4814         }
4815         else if(!cData->lvlOpts[lvlGiveVoice]
4816                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4817         {
4818             if(mn->modes & MODE_CHANOP)
4819             {
4820                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4821                 changes->args[used++].u.member = mn;
4822             }
4823             if(!(mn->modes & MODE_VOICE))
4824             {
4825                 changes->args[used].mode = MODE_VOICE;
4826                 changes->args[used++].u.member = mn;
4827             }
4828         }
4829         else
4830         {
4831             if(mn->modes)
4832             {
4833                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4834                 changes->args[used++].u.member = mn;
4835             }
4836         }
4837     }
4838     changes->argc = used;
4839     modcmd_chanmode_announce(changes);
4840     mod_chanmode_free(changes);
4841     reply("CSMSG_RESYNCED_USERS", channel->name);
4842     return 1;
4843 }
4844
4845 static CHANSERV_FUNC(cmd_seen)
4846 {
4847     struct userData *uData;
4848     struct handle_info *handle;
4849     char seen[INTERVALLEN];
4850
4851     REQUIRE_PARAMS(2);
4852
4853     if(!irccasecmp(argv[1], chanserv->nick))
4854     {
4855         reply("CSMSG_IS_CHANSERV");
4856         return 1;
4857     }
4858
4859     if(!(handle = get_handle_info(argv[1])))
4860     {
4861         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4862         return 0;
4863     }
4864
4865     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4866     {
4867         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4868         return 0;
4869     }
4870
4871     if(uData->present)
4872         reply("CSMSG_USER_PRESENT", handle->handle);
4873     else if(uData->seen)
4874         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4875     else
4876         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4877
4878     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4879         reply("CSMSG_USER_VACATION", handle->handle);
4880
4881     return 1;
4882 }
4883
4884 static MODCMD_FUNC(cmd_names)
4885 {
4886     struct userNode *targ;
4887     struct userData *targData;
4888     unsigned int ii, pos;
4889     char buf[400];
4890
4891     for(ii=pos=0; ii<channel->members.used; ++ii)
4892     {
4893         targ = channel->members.list[ii]->user;
4894         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4895         if(!targData)
4896             continue;
4897         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4898         {
4899             buf[pos] = 0;
4900             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4901             pos = 0;
4902         }
4903         buf[pos++] = ' ';
4904         if(IsUserSuspended(targData))
4905             buf[pos++] = 's';
4906         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4907     }
4908     buf[pos] = 0;
4909     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4910     reply("CSMSG_END_NAMES", channel->name);
4911     return 1;
4912 }
4913
4914 static int
4915 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4916 {
4917     switch(ntype->visible_type)
4918     {
4919     case NOTE_VIS_ALL: return 1;
4920     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4921     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4922     }
4923 }
4924
4925 static int
4926 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4927 {
4928     struct userData *uData;
4929
4930     switch(ntype->set_access_type)
4931     {
4932     case NOTE_SET_CHANNEL_ACCESS:
4933         if(!user->handle_info)
4934             return 0;
4935         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4936             return 0;
4937         return uData->access >= ntype->set_access.min_ulevel;
4938     case NOTE_SET_CHANNEL_SETTER:
4939         return check_user_level(channel, user, lvlSetters, 1, 0);
4940     case NOTE_SET_PRIVILEGED: default:
4941         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4942     }
4943 }
4944
4945 static CHANSERV_FUNC(cmd_note)
4946 {
4947     struct chanData *cData;
4948     struct note *note;
4949     struct note_type *ntype;
4950
4951     cData = channel->channel_info;
4952     if(!cData)
4953     {
4954         reply("CSMSG_NOT_REGISTERED", channel->name);
4955         return 0;
4956     }
4957
4958     /* If no arguments, show all visible notes for the channel. */
4959     if(argc < 2)
4960     {
4961         dict_iterator_t it;
4962         unsigned int count;
4963
4964         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4965         {
4966             note = iter_data(it);
4967             if(!note_type_visible_to_user(cData, note->type, user))
4968                 continue;
4969             if(!count++)
4970                 reply("CSMSG_NOTELIST_HEADER", channel->name);
4971             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4972         }
4973         if(count)
4974             reply("CSMSG_NOTELIST_END", channel->name);
4975         else
4976             reply("CSMSG_NOTELIST_EMPTY", channel->name);
4977     }
4978     /* If one argument, show the named note. */
4979     else if(argc == 2)
4980     {
4981         if((note = dict_find(cData->notes, argv[1], NULL))
4982            && note_type_visible_to_user(cData, note->type, user))
4983         {
4984             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4985         }
4986         else if((ntype = dict_find(note_types, argv[1], NULL))
4987                 && note_type_visible_to_user(NULL, ntype, user))
4988         {
4989             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4990             return 0;
4991         }
4992         else
4993         {
4994             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4995             return 0;
4996         }
4997     }
4998     /* Assume they're trying to set a note. */
4999     else
5000     {
5001         char *note_text;
5002         ntype = dict_find(note_types, argv[1], NULL);
5003         if(!ntype)
5004         {
5005             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5006             return 0;
5007         }
5008         else if(note_type_settable_by_user(channel, ntype, user))
5009         {
5010             note_text = unsplit_string(argv+2, argc-2, NULL);
5011             if((note = dict_find(cData->notes, argv[1], NULL)))
5012                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5013             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5014             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5015
5016             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5017             {
5018                 /* The note is viewable to staff only, so return 0
5019                    to keep the invocation from getting logged (or
5020                    regular users can see it in !events). */
5021                 return 0;
5022             }
5023         }
5024         else
5025         {
5026             reply("CSMSG_NO_ACCESS");
5027             return 0;
5028         }
5029     }
5030     return 1;
5031 }
5032
5033 static CHANSERV_FUNC(cmd_delnote)
5034 {
5035     struct note *note;
5036
5037     REQUIRE_PARAMS(2);
5038     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5039        || !note_type_settable_by_user(channel, note->type, user))
5040     {
5041         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5042         return 0;
5043     }
5044     dict_remove(channel->channel_info->notes, note->type->name);
5045     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5046     return 1;
5047 }
5048
5049 static CHANSERV_FUNC(cmd_events)
5050 {
5051     struct logSearch discrim;
5052     struct logReport report;
5053     unsigned int matches, limit;
5054
5055     limit = (argc > 1) ? atoi(argv[1]) : 10;
5056     if(limit < 1 || limit > 200)
5057         limit = 10;
5058
5059     memset(&discrim, 0, sizeof(discrim));
5060     discrim.masks.bot = chanserv;
5061     discrim.masks.channel_name = channel->name;
5062     if(argc > 2)
5063         discrim.masks.command = argv[2];
5064     discrim.limit = limit;
5065     discrim.max_time = INT_MAX;
5066     discrim.severities = 1 << LOG_COMMAND;
5067     report.reporter = chanserv;
5068     report.user = user;
5069     reply("CSMSG_EVENT_SEARCH_RESULTS");
5070     matches = log_entry_search(&discrim, log_report_entry, &report);
5071     if(matches)
5072         reply("MSG_MATCH_COUNT", matches);
5073     else
5074         reply("MSG_NO_MATCHES");
5075     return 1;
5076 }
5077
5078 static CHANSERV_FUNC(cmd_say)
5079 {
5080     char *msg;
5081     if(channel)
5082     {
5083         REQUIRE_PARAMS(2);
5084         msg = unsplit_string(argv + 1, argc - 1, NULL);
5085         send_channel_message(channel, cmd->parent->bot, "%s", msg);
5086     }
5087     else if(*argv[1] == '*' && argv[1][1] != '\0')
5088     {
5089         struct handle_info *hi;
5090         struct userNode *authed;
5091
5092         REQUIRE_PARAMS(3);
5093         msg = unsplit_string(argv + 2, argc - 2, NULL);
5094
5095         if (!(hi = get_handle_info(argv[1] + 1)))
5096         {
5097             reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5098             return 0;
5099         }
5100
5101         for (authed = hi->users; authed; authed = authed->next_authed)
5102             send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5103     }
5104     else if(GetUserH(argv[1]))
5105     {
5106         REQUIRE_PARAMS(3);
5107         msg = unsplit_string(argv + 2, argc - 2, NULL);
5108         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5109     }
5110     else
5111     {
5112         reply("MSG_NOT_TARGET_NAME");
5113         return 0;
5114     }
5115     return 1;
5116 }
5117
5118 static CHANSERV_FUNC(cmd_emote)
5119 {
5120     char *msg;
5121     assert(argc >= 2);
5122     if(channel)
5123     {
5124         /* CTCP is so annoying. */
5125         msg = unsplit_string(argv + 1, argc - 1, NULL);
5126         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5127     }
5128     else if(*argv[1] == '*' && argv[1][1] != '\0')
5129     {
5130         struct handle_info *hi;
5131         struct userNode *authed;
5132
5133         REQUIRE_PARAMS(3);
5134         msg = unsplit_string(argv + 2, argc - 2, NULL);
5135
5136         if (!(hi = get_handle_info(argv[1] + 1)))
5137         {
5138             reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5139             return 0;
5140         }
5141
5142         for (authed = hi->users; authed; authed = authed->next_authed)
5143             send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5144     }
5145     else if(GetUserH(argv[1]))
5146     {
5147         msg = unsplit_string(argv + 2, argc - 2, NULL);
5148         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5149     }
5150     else
5151     {
5152         reply("MSG_NOT_TARGET_NAME");
5153         return 0;
5154     }
5155     return 1;
5156 }
5157
5158 struct channelList *
5159 chanserv_support_channels(void)
5160 {
5161     return &chanserv_conf.support_channels;
5162 }
5163
5164 static CHANSERV_FUNC(cmd_expire)
5165 {
5166     int channel_count = registered_channels;
5167     expire_channels(NULL);
5168     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5169     return 1;
5170 }
5171
5172 static void
5173 chanserv_expire_suspension(void *data)
5174 {
5175     struct suspended *suspended = data;
5176     struct chanNode *channel;
5177     unsigned int ii;
5178
5179     /* Update the channel registration data structure. */
5180     if(!suspended->expires || (now < suspended->expires))
5181         suspended->revoked = now;
5182     channel = suspended->cData->channel;
5183     suspended->cData->channel = channel;
5184     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5185
5186     /* If appropriate, re-join ChanServ to the channel. */
5187     if(!IsOffChannel(suspended->cData))
5188     {
5189         spamserv_cs_suspend(channel, 0, 0, NULL);
5190         ss_cs_join_channel(channel, 1);
5191     }
5192
5193     /* Mark everyone currently in the channel as present. */
5194     for(ii = 0; ii < channel->members.used; ++ii)
5195     {
5196         struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5197         if(uData)
5198         {
5199             uData->present = 1;
5200             uData->seen = now;
5201         }
5202     }
5203 }
5204
5205 static CHANSERV_FUNC(cmd_csuspend)
5206 {
5207     struct suspended *suspended;
5208     char reason[MAXLEN];
5209     unsigned long expiry, duration;
5210     struct userData *uData;
5211
5212     REQUIRE_PARAMS(3);
5213
5214     if(IsProtected(channel->channel_info))
5215     {
5216         reply("CSMSG_SUSPEND_NODELETE", channel->name);
5217         return 0;
5218     }
5219
5220     if(argv[1][0] == '!')
5221         argv[1]++;
5222     else if(IsSuspended(channel->channel_info))
5223     {
5224         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5225         show_suspension_info(cmd, user, channel->channel_info->suspended);
5226         return 0;
5227     }
5228
5229     if(!strcmp(argv[1], "0"))
5230         expiry = 0;
5231     else if((duration = ParseInterval(argv[1])))
5232         expiry = now + duration;
5233     else
5234     {
5235         reply("MSG_INVALID_DURATION", argv[1]);
5236         return 0;
5237     }
5238
5239     unsplit_string(argv + 2, argc - 2, reason);
5240
5241     suspended = calloc(1, sizeof(*suspended));
5242     suspended->revoked = 0;
5243     suspended->issued = now;
5244     suspended->suspender = strdup(user->handle_info->handle);
5245     suspended->expires = expiry;
5246     suspended->reason = strdup(reason);
5247     suspended->cData = channel->channel_info;
5248     suspended->previous = suspended->cData->suspended;
5249     suspended->cData->suspended = suspended;
5250
5251     if(suspended->expires)
5252         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5253
5254     if(IsSuspended(channel->channel_info))
5255     {
5256         suspended->previous->revoked = now;
5257         if(suspended->previous->expires)
5258             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5259         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5260         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5261     }
5262     else
5263     {
5264         /* Mark all users in channel as absent. */
5265         for(uData = channel->channel_info->users; uData; uData = uData->next)
5266         {
5267             if(uData->present)
5268             {
5269                 uData->seen = now;
5270                 uData->present = 0;
5271             }
5272         }
5273
5274         /* Mark the channel as suspended, then part. */
5275         channel->channel_info->flags |= CHANNEL_SUSPENDED;
5276         spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5277         DelChannelUser(chanserv, channel, suspended->reason, 0);
5278         reply("CSMSG_SUSPENDED", channel->name);
5279         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5280         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5281     }
5282     return 1;
5283 }
5284
5285 static CHANSERV_FUNC(cmd_cunsuspend)
5286 {
5287     struct suspended *suspended;
5288     char message[MAXLEN];
5289
5290     if(!IsSuspended(channel->channel_info))
5291     {
5292         reply("CSMSG_NOT_SUSPENDED", channel->name);
5293         return 0;
5294     }
5295
5296     suspended = channel->channel_info->suspended;
5297
5298     /* Expire the suspension and join ChanServ to the channel. */
5299     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5300     chanserv_expire_suspension(suspended);
5301     reply("CSMSG_UNSUSPENDED", channel->name);
5302     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5303     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5304     return 1;
5305 }
5306
5307 typedef struct chanservSearch
5308 {
5309     char *name;
5310     char *registrar;
5311
5312     unsigned long unvisited;
5313     unsigned long registered;
5314
5315     unsigned long flags;
5316     unsigned int limit;
5317 } *search_t;
5318
5319 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5320
5321 static search_t
5322 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5323 {
5324     search_t search;
5325     unsigned int i;
5326
5327     search = malloc(sizeof(struct chanservSearch));
5328     memset(search, 0, sizeof(*search));
5329     search->limit = 25;
5330
5331     for(i = 0; i < argc; i++)
5332     {
5333         /* Assume all criteria require arguments. */
5334         if(i == (argc - 1))
5335         {
5336             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5337             goto fail;
5338         }
5339
5340         if(!irccasecmp(argv[i], "name"))
5341             search->name = argv[++i];
5342         else if(!irccasecmp(argv[i], "registrar"))
5343             search->registrar = argv[++i];
5344         else if(!irccasecmp(argv[i], "unvisited"))
5345             search->unvisited = ParseInterval(argv[++i]);
5346         else if(!irccasecmp(argv[i], "registered"))
5347             search->registered = ParseInterval(argv[++i]);
5348         else if(!irccasecmp(argv[i], "flags"))
5349         {
5350             i++;
5351             if(!irccasecmp(argv[i], "nodelete"))
5352                 search->flags |= CHANNEL_NODELETE;
5353             else if(!irccasecmp(argv[i], "suspended"))
5354                 search->flags |= CHANNEL_SUSPENDED;
5355             else if(!irccasecmp(argv[i], "unreviewed"))
5356                 search->flags |= CHANNEL_UNREVIEWED;
5357             else
5358             {
5359                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5360                 goto fail;
5361             }
5362         }
5363         else if(!irccasecmp(argv[i], "limit"))
5364             search->limit = strtoul(argv[++i], NULL, 10);
5365         else
5366         {
5367             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5368             goto fail;
5369         }
5370     }
5371
5372     if(search->name && !strcmp(search->name, "*"))
5373         search->name = 0;
5374     if(search->registrar && !strcmp(search->registrar, "*"))
5375         search->registrar = 0;
5376
5377     return search;
5378   fail:
5379     free(search);
5380     return NULL;
5381 }
5382
5383 static int
5384 chanserv_channel_match(struct chanData *channel, search_t search)
5385 {
5386     const char *name = channel->channel->name;
5387     if((search->name && !match_ircglob(name, search->name)) ||
5388        (search->registrar && !channel->registrar) ||
5389        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5390        (search->unvisited && (now - channel->visited) < search->unvisited) ||
5391        (search->registered && (now - channel->registered) > search->registered) ||
5392        (search->flags && ((search->flags & channel->flags) != search->flags)))
5393         return 0;
5394
5395     return 1;
5396 }
5397
5398 static unsigned int
5399 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5400 {
5401     struct chanData *channel;
5402     unsigned int matches = 0;
5403
5404     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5405     {
5406         if(!chanserv_channel_match(channel, search))
5407             continue;
5408         matches++;
5409         smf(channel, data);
5410     }
5411
5412     return matches;
5413 }
5414
5415 static void
5416 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5417 {
5418 }
5419
5420 static void
5421 search_print(struct chanData *channel, void *data)
5422 {
5423     send_message_type(4, data, chanserv, "%s", channel->channel->name);
5424 }
5425
5426 static CHANSERV_FUNC(cmd_search)
5427 {
5428     search_t search;
5429     unsigned int matches;
5430     channel_search_func action;
5431
5432     REQUIRE_PARAMS(3);
5433
5434     if(!irccasecmp(argv[1], "count"))
5435         action = search_count;
5436     else if(!irccasecmp(argv[1], "print"))
5437         action = search_print;
5438     else
5439     {
5440         reply("CSMSG_ACTION_INVALID", argv[1]);
5441         return 0;
5442     }
5443
5444     search = chanserv_search_create(user, argc - 2, argv + 2);
5445     if(!search)
5446         return 0;
5447
5448     if(action == search_count)
5449         search->limit = INT_MAX;
5450
5451     if(action == search_print)
5452         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5453
5454     matches = chanserv_channel_search(search, action, user);
5455
5456     if(matches)
5457         reply("MSG_MATCH_COUNT", matches);
5458     else
5459         reply("MSG_NO_MATCHES");
5460
5461     free(search);
5462     return 1;
5463 }
5464
5465 static CHANSERV_FUNC(cmd_unvisited)
5466 {
5467     struct chanData *cData;
5468     unsigned long interval = chanserv_conf.channel_expire_delay;
5469     char buffer[INTERVALLEN];
5470     unsigned int limit = 25, matches = 0;
5471
5472     if(argc > 1)
5473     {
5474         interval = ParseInterval(argv[1]);
5475         if(argc > 2)
5476             limit = atoi(argv[2]);
5477     }
5478
5479     intervalString(buffer, interval, user->handle_info);
5480     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5481
5482     for(cData = channelList; cData && matches < limit; cData = cData->next)
5483     {
5484         if((now - cData->visited) < interval)
5485             continue;
5486
5487         intervalString(buffer, now - cData->visited, user->handle_info);
5488         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5489         matches++;
5490     }
5491
5492     return 1;
5493 }
5494
5495 static MODCMD_FUNC(chan_opt_defaulttopic)
5496 {
5497     if(argc > 1)
5498     {
5499         char *topic;
5500
5501         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5502         {
5503             reply("CSMSG_TOPIC_LOCKED", channel->name);
5504             return 0;
5505         }
5506
5507         topic = unsplit_string(argv+1, argc-1, NULL);
5508
5509         free(channel->channel_info->topic);
5510         if(topic[0] == '*' && topic[1] == 0)
5511         {
5512             topic = channel->channel_info->topic = NULL;
5513         }
5514         else
5515         {
5516             topic = channel->channel_info->topic = strdup(topic);
5517             if(channel->channel_info->topic_mask
5518                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5519                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5520         }
5521         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5522     }
5523
5524     if(channel->channel_info->topic)
5525         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5526     else
5527         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5528     return 1;
5529 }
5530
5531 static MODCMD_FUNC(chan_opt_topicmask)
5532 {
5533     if(argc > 1)
5534     {
5535         struct chanData *cData = channel->channel_info;
5536         char *mask;
5537
5538         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5539         {
5540             reply("CSMSG_TOPIC_LOCKED", channel->name);
5541             return 0;
5542         }
5543
5544         mask = unsplit_string(argv+1, argc-1, NULL);
5545
5546         if(cData->topic_mask)
5547             free(cData->topic_mask);
5548         if(mask[0] == '*' && mask[1] == 0)
5549         {
5550             cData->topic_mask = 0;
5551         }
5552         else
5553         {
5554             cData->topic_mask = strdup(mask);
5555             if(!cData->topic)
5556                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5557             else if(!match_ircglob(cData->topic, cData->topic_mask))
5558                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5559         }
5560     }
5561
5562     if(channel->channel_info->topic_mask)
5563         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5564     else
5565         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5566     return 1;
5567 }
5568
5569 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5570 {
5571     if(argc > 1)
5572     {
5573         char *greeting = unsplit_string(argv+1, argc-1, NULL);
5574         char *previous;
5575
5576         previous = *data;
5577         if(greeting[0] == '*' && greeting[1] == 0)
5578             *data = NULL;
5579         else
5580         {
5581             unsigned int length = strlen(greeting);
5582             if(length > chanserv_conf.greeting_length)
5583             {
5584                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5585                 return 0;
5586             }
5587             *data = strdup(greeting);
5588         }
5589         if(previous)
5590             free(previous);
5591     }
5592
5593     if(*data)
5594         reply(name, *data);
5595     else
5596         reply(name, user_find_message(user, "MSG_NONE"));
5597     return 1;
5598 }
5599
5600 static MODCMD_FUNC(chan_opt_greeting)
5601 {
5602     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5603 }
5604
5605 static MODCMD_FUNC(chan_opt_usergreeting)
5606 {
5607     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5608 }
5609
5610 static MODCMD_FUNC(chan_opt_modes)
5611 {
5612     struct mod_chanmode *new_modes;
5613     char modes[MAXLEN];
5614
5615     if(argc > 1)
5616     {
5617         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5618         {
5619             reply("CSMSG_NO_ACCESS");
5620             return 0;
5621         }
5622         if(argv[1][0] == '*' && argv[1][1] == 0)
5623         {
5624             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5625         }
5626         else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5627         {
5628             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5629             return 0;
5630         }
5631         else if(new_modes->argc > 1)
5632         {
5633             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5634             mod_chanmode_free(new_modes);
5635             return 0;
5636         }
5637         else
5638         {
5639             channel->channel_info->modes = *new_modes;
5640             modcmd_chanmode_announce(new_modes);
5641             mod_chanmode_free(new_modes);
5642         }
5643     }
5644
5645     mod_chanmode_format(&channel->channel_info->modes, modes);
5646     if(modes[0])
5647         reply("CSMSG_SET_MODES", modes);
5648     else
5649         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5650     return 1;
5651 }
5652
5653 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5654 static int
5655 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5656 {
5657     struct chanData *cData = channel->channel_info;
5658     int value;
5659
5660     if(argc > 1)
5661     {
5662         /* Set flag according to value. */
5663         if(enabled_string(argv[1]))
5664         {
5665             cData->flags |= mask;
5666             value = 1;
5667         }
5668         else if(disabled_string(argv[1]))
5669         {
5670             cData->flags &= ~mask;
5671             value = 0;
5672         }
5673         else
5674         {
5675             reply("MSG_INVALID_BINARY", argv[1]);
5676             return 0;
5677         }
5678     }
5679     else
5680     {
5681         /* Find current option value. */
5682         value = (cData->flags & mask) ? 1 : 0;
5683     }
5684
5685     if(value)
5686         reply(name, user_find_message(user, "MSG_ON"));
5687     else
5688         reply(name, user_find_message(user, "MSG_OFF"));
5689     return 1;
5690 }
5691
5692 static MODCMD_FUNC(chan_opt_nodelete)
5693 {
5694     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5695     {
5696         reply("MSG_SETTING_PRIVILEGED", argv[0]);
5697         return 0;
5698     }
5699
5700     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5701 }
5702
5703 static MODCMD_FUNC(chan_opt_dynlimit)
5704 {
5705     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5706 }
5707
5708 static MODCMD_FUNC(chan_opt_offchannel)
5709 {
5710     struct chanData *cData = channel->channel_info;
5711     int value;
5712
5713     if(argc > 1)
5714     {
5715         /* Set flag according to value. */
5716         if(enabled_string(argv[1]))
5717         {
5718             if(!IsOffChannel(cData))
5719                 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5720             cData->flags |= CHANNEL_OFFCHANNEL;
5721             value = 1;
5722         }
5723         else if(disabled_string(argv[1]))
5724         {
5725             if(IsOffChannel(cData))
5726             {
5727                 struct mod_chanmode change;
5728                 mod_chanmode_init(&change);
5729                 change.argc = 1;
5730                 change.args[0].mode = MODE_CHANOP;
5731                 change.args[0].u.member = AddChannelUser(chanserv, channel);
5732                 mod_chanmode_announce(chanserv, channel, &change);
5733             }
5734             cData->flags &= ~CHANNEL_OFFCHANNEL;
5735             value = 0;
5736         }
5737         else
5738         {
5739             reply("MSG_INVALID_BINARY", argv[1]);
5740             return 0;
5741         }
5742     }
5743     else
5744     {
5745         /* Find current option value. */
5746         value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5747     }
5748
5749     if(value)
5750         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5751     else
5752         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5753     return 1;
5754 }
5755
5756 static MODCMD_FUNC(chan_opt_unreviewed)
5757 {
5758     struct chanData *cData = channel->channel_info;
5759     int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5760
5761     if(argc > 1)
5762     {
5763         int new_value;
5764
5765         /* The two directions can have different ACLs. */
5766         if(enabled_string(argv[1]))
5767             new_value = 1;
5768         else if(disabled_string(argv[1]))
5769             new_value = 0;
5770         else
5771         {
5772             reply("MSG_INVALID_BINARY", argv[1]);
5773             return 0;
5774         }
5775
5776         if (new_value != value)
5777         {
5778             struct svccmd *subcmd;
5779             char subcmd_name[32];
5780
5781             snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5782             subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5783             if(!subcmd)
5784             {
5785                 reply("MSG_COMMAND_DISABLED", subcmd_name);
5786                 return 0;
5787             }
5788             else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5789                 return 0;
5790
5791             if (new_value)
5792                 cData->flags |= CHANNEL_UNREVIEWED;
5793             else
5794             {
5795                 free(cData->registrar);
5796                 cData->registrar = strdup(user->handle_info->handle);
5797                 cData->flags &= ~CHANNEL_UNREVIEWED;
5798             }
5799             value = new_value;
5800         }
5801     }
5802
5803     if(value)
5804         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5805     else
5806         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5807     return 1;
5808 }
5809
5810 static MODCMD_FUNC(chan_opt_defaults)
5811 {
5812     struct userData *uData;
5813     struct chanData *cData;
5814     const char *confirm;
5815     enum levelOption lvlOpt;
5816     enum charOption chOpt;
5817
5818     cData = channel->channel_info;
5819     uData = GetChannelUser(cData, user->handle_info);
5820     if(!uData || (uData->access < UL_OWNER))
5821     {
5822         reply("CSMSG_OWNER_DEFAULTS", channel->name);
5823         return 0;
5824     }
5825     confirm = make_confirmation_string(uData);
5826     if((argc < 2) || strcmp(argv[1], confirm))
5827     {
5828         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5829         return 0;
5830     }
5831     cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5832         | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5833     cData->modes = chanserv_conf.default_modes;
5834     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5835         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5836     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5837         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5838     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5839     return 1;
5840 }
5841
5842 static int
5843 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5844 {
5845     struct chanData *cData = channel->channel_info;
5846     struct userData *uData;
5847     unsigned short value;
5848
5849     if(argc > 1)
5850     {
5851         if(!check_user_level(channel, user, option, 1, 1))
5852         {
5853             reply("CSMSG_CANNOT_SET");
5854             return 0;
5855         }
5856         value = user_level_from_name(argv[1], UL_OWNER+1);
5857         if(!value && strcmp(argv[1], "0"))
5858         {
5859             reply("CSMSG_INVALID_ACCESS", argv[1]);
5860             return 0;
5861         }
5862         uData = GetChannelUser(cData, user->handle_info);
5863         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5864         {
5865             reply("CSMSG_BAD_SETLEVEL");
5866             return 0;
5867         }
5868         switch(option)
5869         {
5870         case lvlGiveVoice:
5871             if(value > cData->lvlOpts[lvlGiveOps])
5872             {
5873                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5874                 return 0;
5875             }
5876             break;
5877         case lvlGiveOps:
5878             if(value < cData->lvlOpts[lvlGiveVoice])
5879             {
5880                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5881                 return 0;
5882             }
5883             break;
5884         case lvlSetters:
5885             /* This test only applies to owners, since non-owners
5886              * trying to set an option to above their level get caught
5887              * by the CSMSG_BAD_SETLEVEL test above.
5888              */
5889             if(value > uData->access)
5890             {
5891                 reply("CSMSG_BAD_SETTERS");
5892                 return 0;
5893             }
5894             break;
5895         default:
5896             break;
5897         }
5898         cData->lvlOpts[option] = value;
5899     }
5900     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5901     return argc > 1;
5902 }
5903
5904 static MODCMD_FUNC(chan_opt_enfops)
5905 {
5906     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5907 }
5908
5909 static MODCMD_FUNC(chan_opt_giveops)
5910 {
5911     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5912 }
5913
5914 static MODCMD_FUNC(chan_opt_enfmodes)
5915 {
5916     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5917 }
5918
5919 static MODCMD_FUNC(chan_opt_enftopic)
5920 {
5921     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5922 }
5923
5924 static MODCMD_FUNC(chan_opt_pubcmd)
5925 {
5926     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5927 }
5928
5929 static MODCMD_FUNC(chan_opt_setters)
5930 {
5931     return channel_level_option(lvlSetters, CSFUNC_ARGS);
5932 }
5933
5934 static MODCMD_FUNC(chan_opt_ctcpusers)
5935 {
5936     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5937 }
5938
5939 static MODCMD_FUNC(chan_opt_userinfo)
5940 {
5941     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5942 }
5943
5944 static MODCMD_FUNC(chan_opt_givevoice)
5945 {
5946     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5947 }
5948
5949 static MODCMD_FUNC(chan_opt_topicsnarf)
5950 {
5951     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5952 }
5953
5954 static MODCMD_FUNC(chan_opt_vote)
5955 {
5956     return channel_level_option(lvlVote, CSFUNC_ARGS);
5957 }
5958
5959 static MODCMD_FUNC(chan_opt_inviteme)
5960 {
5961     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5962 }
5963
5964 static int
5965 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5966 {
5967     struct chanData *cData = channel->channel_info;
5968     int count = charOptions[option].count, idx;
5969
5970     if(argc > 1)
5971     {
5972         idx = atoi(argv[1]);
5973
5974         if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5975         {
5976             reply("CSMSG_INVALID_NUMERIC", idx);
5977             /* Show possible values. */
5978             for(idx = 0; idx < count; idx++)
5979                 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5980             return 0;
5981         }
5982
5983         cData->chOpts[option] = charOptions[option].values[idx].value;
5984     }
5985     else
5986     {
5987         /* Find current option value. */
5988       find_value:
5989         for(idx = 0;
5990             (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5991             idx++);
5992         if(idx == count)
5993         {
5994             /* Somehow, the option value is corrupt; reset it to the default. */
5995             cData->chOpts[option] = charOptions[option].default_value;
5996             goto find_value;
5997         }
5998     }
5999
6000     reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6001     return 1;
6002 }
6003
6004 static MODCMD_FUNC(chan_opt_protect)
6005 {
6006     return channel_multiple_option(chProtect, CSFUNC_ARGS);
6007 }
6008
6009 static MODCMD_FUNC(chan_opt_toys)
6010 {
6011     return channel_multiple_option(chToys, CSFUNC_ARGS);
6012 }
6013
6014 static MODCMD_FUNC(chan_opt_ctcpreaction)
6015 {
6016     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6017 }
6018
6019 static MODCMD_FUNC(chan_opt_topicrefresh)
6020 {
6021     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6022 }
6023
6024 static struct svccmd_list set_shows_list;
6025
6026 static void
6027 handle_svccmd_unbind(struct svccmd *target) {
6028     unsigned int ii;
6029     for(ii=0; ii<set_shows_list.used; ++ii)
6030         if(target == set_shows_list.list[ii])
6031             set_shows_list.used = 0;
6032 }
6033
6034 static CHANSERV_FUNC(cmd_set)
6035 {
6036     struct svccmd *subcmd;
6037     char buf[MAXLEN];
6038     unsigned int ii;
6039
6040     /* Check if we need to (re-)initialize set_shows_list. */
6041     if(!set_shows_list.used)
6042     {
6043         if(!set_shows_list.size)
6044         {
6045             set_shows_list.size = chanserv_conf.set_shows->used;
6046             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6047         }
6048         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6049         {
6050             const char *name = chanserv_conf.set_shows->list[ii];
6051             sprintf(buf, "%s %s", argv[0], name);
6052             subcmd = dict_find(cmd->parent->commands, buf, NULL);
6053             if(!subcmd)
6054             {
6055                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6056                 continue;
6057             }
6058             svccmd_list_append(&set_shows_list, subcmd);
6059         }
6060     }
6061
6062     if(argc < 2)
6063     {
6064         reply("CSMSG_CHANNEL_OPTIONS");
6065         for(ii = 0; ii < set_shows_list.used; ii++)
6066         {
6067             subcmd = set_shows_list.list[ii];
6068             subcmd->command->func(user, channel, 1, argv+1, subcmd);
6069         }
6070         return 1;
6071     }
6072
6073     sprintf(buf, "%s %s", argv[0], argv[1]);
6074     subcmd = dict_find(cmd->parent->commands, buf, NULL);
6075     if(!subcmd)
6076     {
6077         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6078         return 0;
6079     }
6080     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6081     {
6082         reply("CSMSG_NO_ACCESS");
6083         return 0;
6084     }
6085
6086     argv[0] = "";
6087     argv[1] = buf;
6088     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6089 }
6090
6091 static int
6092 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6093 {
6094     struct userData *uData;
6095
6096     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6097     if(!uData)
6098     {
6099         reply("CSMSG_NOT_USER", channel->name);
6100         return 0;
6101     }
6102
6103     if(argc < 2)
6104     {
6105         /* Just show current option value. */
6106     }
6107     else if(enabled_string(argv[1]))
6108     {
6109         uData->flags |= mask;
6110     }
6111     else if(disabled_string(argv[1]))
6112     {
6113         uData->flags &= ~mask;
6114     }
6115     else
6116     {
6117         reply("MSG_INVALID_BINARY", argv[1]);
6118         return 0;
6119     }
6120
6121     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6122     return 1;
6123 }
6124
6125 static MODCMD_FUNC(user_opt_noautoop)
6126 {
6127     struct userData *uData;
6128
6129     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6130     if(!uData)
6131     {
6132         reply("CSMSG_NOT_USER", channel->name);
6133         return 0;
6134     }
6135     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6136         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6137     else
6138         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6139 }
6140
6141 static MODCMD_FUNC(user_opt_autoinvite)
6142 {
6143     if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6144     {
6145         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6146     }
6147     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6148 }
6149
6150 static MODCMD_FUNC(user_opt_info)
6151 {
6152     struct userData *uData;
6153     char *infoline;
6154
6155     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6156
6157     if(!uData)
6158     {
6159         /* If they got past the command restrictions (which require access)
6160          * but fail this test, we have some fool with security override on.
6161          */
6162         reply("CSMSG_NOT_USER", channel->name);
6163         return 0;
6164     }
6165
6166     if(argc > 1)
6167     {
6168         size_t bp;
6169         infoline = unsplit_string(argv + 1, argc - 1, NULL);
6170         if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6171         {
6172             reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6173             return 0;
6174         }
6175         bp = strcspn(infoline, "\001");
6176         if(infoline[bp])
6177         {
6178             reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6179             return 0;
6180         }
6181         if(uData->info)
6182             free(uData->info);
6183         if(infoline[0] == '*' && infoline[1] == 0)
6184             uData->info = NULL;
6185         else
6186             uData->info = strdup(infoline);
6187     }
6188     if(uData->info)
6189         reply("CSMSG_USET_INFO", uData->info);
6190     else
6191         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6192     return 1;
6193 }
6194
6195 struct svccmd_list uset_shows_list;
6196
6197 static CHANSERV_FUNC(cmd_uset)
6198 {
6199     struct svccmd *subcmd;
6200     char buf[MAXLEN];
6201     unsigned int ii;
6202
6203     /* Check if we need to (re-)initialize uset_shows_list. */
6204     if(!uset_shows_list.used)
6205     {
6206         char *options[] =
6207         {
6208             "NoAutoOp", "AutoInvite", "Info"
6209         };
6210
6211         if(!uset_shows_list.size)
6212         {
6213             uset_shows_list.size = ArrayLength(options);
6214             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6215         }
6216         for(ii = 0; ii < ArrayLength(options); ii++)
6217         {
6218             const char *name = options[ii];
6219             sprintf(buf, "%s %s", argv[0], name);
6220             subcmd = dict_find(cmd->parent->commands, buf, NULL);
6221             if(!subcmd)
6222             {
6223                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6224                 continue;
6225             }
6226             svccmd_list_append(&uset_shows_list, subcmd);
6227         }
6228     }
6229
6230     if(argc < 2)
6231     {
6232         /* Do this so options are presented in a consistent order. */
6233         reply("CSMSG_USER_OPTIONS");
6234         for(ii = 0; ii < uset_shows_list.used; ii++)
6235             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6236         return 1;
6237     }
6238
6239     sprintf(buf, "%s %s", argv[0], argv[1]);
6240     subcmd = dict_find(cmd->parent->commands, buf, NULL);
6241     if(!subcmd)
6242     {
6243         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6244         return 0;
6245     }
6246
6247     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6248 }
6249
6250 static CHANSERV_FUNC(cmd_giveownership)
6251 {
6252     struct handle_info *new_owner_hi;
6253     struct userData *new_owner;
6254     struct userData *curr_user;
6255     struct userData *invoker;
6256     struct chanData *cData = channel->channel_info;
6257     struct do_not_register *dnr;
6258     const char *confirm;
6259     unsigned int force;
6260     unsigned short co_access;
6261     char reason[MAXLEN];
6262
6263     REQUIRE_PARAMS(2);
6264     curr_user = GetChannelAccess(cData, user->handle_info);
6265     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6266     if(!curr_user || (curr_user->access != UL_OWNER))
6267     {
6268         struct userData *owner = NULL;
6269         for(curr_user = channel->channel_info->users;
6270             curr_user;
6271             curr_user = curr_user->next)
6272         {
6273             if(curr_user->access != UL_OWNER)
6274                 continue;
6275             if(owner)
6276             {
6277                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6278                 return 0;
6279             }
6280             owner = curr_user;
6281         }
6282         curr_user = owner;
6283     }
6284     else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6285     {
6286         char delay[INTERVALLEN];
6287         intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6288         reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6289         return 0;
6290     }
6291     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6292         return 0;
6293     if(new_owner_hi == user->handle_info)
6294     {
6295         reply("CSMSG_NO_TRANSFER_SELF");
6296         return 0;
6297     }
6298     new_owner = GetChannelAccess(cData, new_owner_hi);
6299     if(!new_owner)
6300     {
6301         if(force)
6302         {
6303             new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6304         }
6305         else
6306         {
6307             reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6308             return 0;
6309         }
6310     }
6311     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6312     {
6313         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6314         return 0;
6315     }
6316     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6317         if(!IsHelping(user))
6318             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6319         else
6320             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6321         return 0;
6322     }
6323     invoker = GetChannelUser(cData, user->handle_info);
6324     if(invoker->access <= UL_OWNER)
6325     {
6326         confirm = make_confirmation_string(curr_user);
6327         if((argc < 3) || strcmp(argv[2], confirm))
6328         {
6329             reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6330             return 0;
6331         }
6332     }
6333     if(new_owner->access >= UL_COOWNER)
6334         co_access = new_owner->access;
6335     else
6336         co_access = UL_COOWNER;
6337     new_owner->access = UL_OWNER;
6338     if(curr_user)
6339         curr_user->access = co_access;
6340     cData->ownerTransfer = now;
6341     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6342     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6343     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6344     return 1;
6345 }
6346
6347 static CHANSERV_FUNC(cmd_suspend)
6348 {
6349     struct handle_info *hi;
6350     struct userData *actor, *real_actor, *target;
6351     unsigned int override = 0;
6352
6353     REQUIRE_PARAMS(2);
6354     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6355     actor = GetChannelUser(channel->channel_info, user->handle_info);
6356     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6357     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6358     {
6359         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6360         return 0;
6361     }
6362     if(target->access >= actor->access)
6363     {
6364         reply("MSG_USER_OUTRANKED", hi->handle);
6365         return 0;
6366     }
6367     if(target->flags & USER_SUSPENDED)
6368     {
6369         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6370         return 0;
6371     }
6372     if(target->present)
6373     {
6374         target->present = 0;
6375         target->seen = now;
6376     }
6377     if(!real_actor || target->access >= real_actor->access)
6378         override = CMD_LOG_OVERRIDE;
6379     target->flags |= USER_SUSPENDED;
6380     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6381     return 1 | override;
6382 }
6383
6384 static CHANSERV_FUNC(cmd_unsuspend)
6385 {
6386     struct handle_info *hi;
6387     struct userData *actor, *real_actor, *target;
6388     unsigned int override = 0;
6389
6390     REQUIRE_PARAMS(2);
6391     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6392     actor = GetChannelUser(channel->channel_info, user->handle_info);
6393     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6394     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6395     {
6396         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6397         return 0;
6398     }
6399     if(target->access >= actor->access)
6400     {
6401         reply("MSG_USER_OUTRANKED", hi->handle);
6402         return 0;
6403     }
6404     if(!(target->flags & USER_SUSPENDED))
6405     {
6406         reply("CSMSG_NOT_SUSPENDED", hi->handle);
6407         return 0;
6408     }
6409     if(!real_actor || target->access >= real_actor->access)
6410         override = CMD_LOG_OVERRIDE;
6411     target->flags &= ~USER_SUSPENDED;
6412     scan_user_presence(target, NULL);
6413     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6414     return 1 | override;
6415 }
6416
6417 static MODCMD_FUNC(cmd_deleteme)
6418 {
6419     struct handle_info *hi;
6420     struct userData *target;
6421     const char *confirm_string;
6422     unsigned short access_level;
6423     char *channel_name;
6424
6425     hi = user->handle_info;
6426     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6427     {
6428         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6429         return 0;
6430     }
6431     if(target->access == UL_OWNER)
6432     {
6433         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6434         return 0;
6435     }
6436     confirm_string = make_confirmation_string(target);
6437     if((argc < 2) || strcmp(argv[1], confirm_string))
6438     {
6439         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6440         return 0;
6441     }
6442     access_level = target->access;
6443     channel_name = strdup(channel->name);
6444     del_channel_user(target, 1);
6445     reply("CSMSG_DELETED_YOU", access_level, channel_name);
6446     free(channel_name);
6447     return 1;
6448 }
6449
6450 static CHANSERV_FUNC(cmd_addvote)
6451 {
6452     struct chanData *cData = channel->channel_info;
6453     struct userData *uData, *target;
6454     struct handle_info *hi;
6455     if (!cData) return 0;
6456     REQUIRE_PARAMS(2);
6457     hi = user->handle_info;
6458     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6459     {
6460         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6461         return 0;
6462     }
6463     if(target->access < 300) {
6464         reply("CSMSG_NO_ACCESS");
6465         return 0;
6466     }
6467     if (cData->vote) {
6468         reply("CSMSG_ADDVOTE_FULL");
6469         return 0;
6470     }
6471     char *msg;
6472     msg = unsplit_string(argv + 1, argc - 1, NULL);
6473     cData->vote = strdup(msg);
6474     cData->vote_start=0;
6475     dict_delete(cData->vote_options);
6476     cData->vote_options = dict_new();
6477     dict_set_free_data(cData->vote_options, free_vote_options);
6478     for(uData = channel->channel_info->users; uData; uData = uData->next)
6479     {
6480         uData->voted = 0;
6481         uData->votefor = 0;
6482     }
6483     reply("CSMSG_ADDVOTE_DONE");
6484     return 1;
6485 }
6486
6487 static CHANSERV_FUNC(cmd_delvote)
6488 {
6489     struct chanData *cData = channel->channel_info;
6490     struct userData *target;
6491     struct handle_info *hi;
6492     if (!cData) return 0;
6493     hi = user->handle_info;
6494     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6495     {
6496         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6497         return 0;
6498     }
6499     if(target->access < 300) {
6500         reply("CSMSG_NO_ACCESS");
6501         return 0;
6502     }
6503     if (!cData->vote) {
6504         reply("CSMSG_NO_VOTE");
6505         return 0;
6506     }
6507     free(cData->vote);
6508     cData->vote = NULL;
6509     reply("CSMSG_DELVOTE_DONE");
6510     return 1;
6511 }
6512
6513 static CHANSERV_FUNC(cmd_addoption)
6514 {
6515     struct chanData *cData = channel->channel_info;
6516     struct userData *target;
6517     struct handle_info *hi;
6518     if (!cData) return 0;
6519     REQUIRE_PARAMS(2);
6520     hi = user->handle_info;
6521     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6522     {
6523         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6524         return 0;
6525     }
6526     if(target->access < 300) {
6527         reply("CSMSG_NO_ACCESS");
6528         return 0;
6529     }
6530     if (!cData->vote) {
6531         reply("CSMSG_NO_VOTE");
6532         return 0;
6533     }
6534     
6535     char *msg;
6536     
6537     msg = unsplit_string(argv + 1, argc - 1, NULL);
6538     
6539     dict_iterator_t it;
6540     unsigned int lastid = 1;
6541     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6542         struct vote_option *cvOpt = iter_data(it);
6543         if(cvOpt->option_id > lastid)
6544             lastid = cvOpt->option_id;
6545     }
6546     struct vote_option *vOpt;
6547     vOpt = calloc(1, sizeof(*vOpt));
6548     vOpt->name = strdup(msg);
6549     vOpt->option_id = (lastid + 1);
6550     char str[50];
6551     sprintf(str,"%i",(lastid + 1));
6552     vOpt->option_str = strdup(str);
6553     vOpt->voted = 0;
6554     dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6555     
6556     reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6557     return 1;
6558 }
6559
6560 static CHANSERV_FUNC(cmd_deloption)
6561 {
6562     struct chanData *cData = channel->channel_info;
6563     struct userData *uData, *target;
6564     struct handle_info *hi;
6565     if (!cData) return 0;
6566     REQUIRE_PARAMS(2);
6567     hi = user->handle_info;
6568     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6569     {
6570         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6571         return 0;
6572     }
6573     if(target->access < 300) {
6574         reply("CSMSG_NO_ACCESS");
6575         return 0;
6576     }
6577     if (!cData->vote) {
6578         reply("CSMSG_NO_VOTE");
6579         return 0;
6580     }
6581     if(cData->vote_start) {
6582         if(dict_size(cData->vote_options) < 3) {
6583             reply("CSMSG_VOTE_NEED_OPTIONS");
6584             return 0;
6585         }
6586     }
6587     
6588     int find_id = atoi(argv[1]);
6589     int ii = 0;
6590     unsigned int found = 0;
6591     dict_iterator_t it;
6592     
6593     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6594         ii++;
6595         if (find_id == ii) {
6596             struct vote_option *vOpt = iter_data(it);
6597             found = vOpt->option_id;
6598             char str[50];
6599             sprintf(str,"%i",vOpt->option_id);
6600             dict_remove(cData->vote_options, str);
6601         }
6602     }
6603     
6604     if(found > 0) {
6605         for(uData = channel->channel_info->users; uData; uData = uData->next) {
6606             if(uData->votefor == found) {
6607                 uData->voted = 0;
6608                 uData->votefor = 0;
6609             }
6610         }
6611         reply("CSMSG_DELOPTION_DONE");
6612         return 1;
6613     } else {
6614         reply("CSMSG_DELOPTION_NONE");
6615         return 0;
6616     }
6617 }
6618
6619 static CHANSERV_FUNC(cmd_vote)
6620 {
6621     struct chanData *cData = channel->channel_info;
6622     struct userData *target;
6623     struct handle_info *hi;
6624     unsigned int votedfor = 0;
6625     char *votedfor_str = NULL;
6626     
6627     if (!cData || !cData->vote) {
6628         reply("CSMSG_NO_VOTE");
6629         return 0;
6630     }
6631     if(argc > 1 && cData->vote_start) {
6632         hi = user->handle_info;
6633         if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6634         {
6635             reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6636             return 0;
6637         }
6638         if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6639             reply("CSMSG_NO_ACCESS");
6640             return 0;
6641         }
6642         if(target->voted) {
6643             reply("CSMSG_VOTE_VOTED");
6644             return 0;
6645         }
6646         int find_id = atoi(argv[1]);
6647         int ii = 0;
6648         dict_iterator_t it;
6649         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6650             ii++;
6651             if (find_id == ii) {
6652                 struct vote_option *vOpt = iter_data(it);
6653                 vOpt->voted++;
6654                 target->voted = 1;
6655                 target->votefor = vOpt->option_id;
6656                 votedfor = vOpt->option_id;
6657                 votedfor_str = vOpt->name;
6658             }
6659         }
6660         if(votedfor == 0) {
6661             reply("CSMSG_VOTE_INVALID");
6662             return 0;
6663         }
6664     }
6665     if (!cData->vote_start) {
6666         reply("CSMSG_VOTE_NOT_STARTED");
6667     }
6668     reply("CSMSG_VOTE_QUESTION",cData->vote);
6669     
6670     unsigned int voteid = 0;
6671     dict_iterator_t it;
6672     
6673     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6674         struct vote_option *vOpt = iter_data(it);
6675         voteid++;
6676         reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6677     }
6678     if(argc > 1 && cData->vote_start && votedfor_str) {
6679         reply("CSMSG_VOTE_DONE",votedfor_str);
6680     }
6681     return 1;
6682 }
6683
6684 static CHANSERV_FUNC(cmd_startvote)
6685 {
6686     struct chanData *cData = channel->channel_info;
6687     struct userData *target;
6688     struct handle_info *hi;
6689     if (!cData) return 0;
6690     hi = user->handle_info;
6691     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6692     {
6693         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6694         return 0;
6695     }
6696     if(target->access < 300) {
6697         reply("CSMSG_NO_ACCESS");
6698         return 0;
6699     }
6700     if (!cData->vote) {
6701         reply("CSMSG_NO_VOTE");
6702         return 0;
6703     }
6704     if(cData->vote_start) {
6705         reply("CSMSG_STARTVOTE_RUNNING");
6706         return 0;
6707     }
6708     if(dict_size(cData->vote_options) < 2) {
6709         reply("CSMSG_VOTE_NEED_OPTIONS");
6710         return 0;
6711     }
6712     cData->vote_start = 1;
6713     char response[MAXLEN];
6714     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6715     irc_privmsg(cmd->parent->bot, channel->name, response);
6716     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6717     irc_privmsg(cmd->parent->bot, channel->name, response);
6718     unsigned int voteid = 0;
6719     dict_iterator_t it;
6720     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6721         struct vote_option *vOpt = iter_data(it);
6722         voteid++;
6723         sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6724         irc_privmsg(cmd->parent->bot, channel->name, response);
6725     }
6726     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6727     irc_privmsg(cmd->parent->bot, channel->name, response);
6728     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6729     irc_privmsg(cmd->parent->bot, channel->name, response);
6730     return 1;
6731 }
6732
6733 static CHANSERV_FUNC(cmd_endvote)
6734 {
6735     struct chanData *cData = channel->channel_info;
6736     struct userData *target;
6737     struct handle_info *hi;
6738     if (!cData) return 0;
6739     hi = user->handle_info;
6740     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6741     {
6742         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6743         return 0;
6744     }
6745     if(target->access < 300) {
6746         reply("CSMSG_NO_ACCESS");
6747         return 0;
6748     }
6749     if (!cData->vote) {
6750         reply("CSMSG_NO_VOTE");
6751         return 0;
6752     }
6753     if(!cData->vote_start) {
6754         reply("CSMSG_ENDVOTE_STOPPED");
6755         return 0;
6756     }
6757     cData->vote_start = 0;
6758     reply("CSMSG_ENDVOTE_DONE");
6759     return 1;
6760 }
6761
6762 static CHANSERV_FUNC(cmd_voteresults)
6763 {
6764     struct chanData *cData = channel->channel_info;
6765     struct userData *target;
6766     struct handle_info *hi;
6767     if (!cData) return 0;
6768     if (!cData->vote) {
6769         reply("CSMSG_NO_VOTE");
6770         return 0;
6771     }
6772     if (argc > 1 && !irccasecmp(argv[1], "*")) {
6773         hi = user->handle_info;
6774         if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6775         {
6776             reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6777             return 0;
6778         }
6779         if(target->access < 300) {
6780             reply("CSMSG_NO_ACCESS");
6781             return 0;
6782         }
6783         char response[MAXLEN];
6784         sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6785         irc_privmsg(cmd->parent->bot, channel->name, response);
6786         unsigned int voteid = 0;
6787         dict_iterator_t it;
6788         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6789             struct vote_option *vOpt = iter_data(it);
6790             voteid++;
6791             sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6792             irc_privmsg(cmd->parent->bot, channel->name, response);
6793         }
6794     } else {
6795         reply("CSMSG_VOTE_QUESTION",cData->vote);
6796         unsigned int voteid = 0;
6797        dict_iterator_t it;
6798         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6799             struct vote_option *vOpt = iter_data(it);
6800             voteid++;
6801             reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6802         }
6803     }
6804     return 1;
6805 }
6806
6807 static void
6808 chanserv_refresh_topics(UNUSED_ARG(void *data))
6809 {
6810     unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6811     struct chanData *cData;
6812     char opt;
6813
6814     for(cData = channelList; cData; cData = cData->next)
6815     {
6816         if(IsSuspended(cData))
6817             continue;
6818         opt = cData->chOpts[chTopicRefresh];
6819         if(opt == 'n')
6820             continue;
6821         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6822             continue;
6823         if(cData->topic)
6824             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6825         cData->last_refresh = refresh_num;
6826     }
6827     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6828 }
6829
6830 static CHANSERV_FUNC(cmd_unf)
6831 {
6832     if(channel)
6833     {
6834         char response[MAXLEN];
6835         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6836         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6837         irc_privmsg(cmd->parent->bot, channel->name, response);
6838     }
6839     else
6840         reply("CSMSG_UNF_RESPONSE");
6841     return 1;
6842 }
6843
6844 static CHANSERV_FUNC(cmd_ping)
6845 {
6846     if(channel)
6847     {
6848         char response[MAXLEN];
6849         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6850         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6851         irc_privmsg(cmd->parent->bot, channel->name, response);
6852     }
6853     else
6854         reply("CSMSG_PING_RESPONSE");
6855     return 1;
6856 }
6857
6858 static CHANSERV_FUNC(cmd_wut)
6859 {
6860     if(channel)
6861     {
6862         char response[MAXLEN];
6863         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6864         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6865         irc_privmsg(cmd->parent->bot, channel->name, response);
6866     }
6867     else
6868         reply("CSMSG_WUT_RESPONSE");
6869     return 1;
6870 }
6871
6872 static CHANSERV_FUNC(cmd_8ball)
6873 {
6874     unsigned int i, j, accum;
6875     const char *resp;
6876
6877     REQUIRE_PARAMS(2);
6878     accum = 0;
6879     for(i=1; i<argc; i++)
6880         for(j=0; argv[i][j]; j++)
6881             accum = (accum << 5) - accum + toupper(argv[i][j]);
6882     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6883     if(channel)
6884     {
6885         char response[MAXLEN];
6886         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6887         irc_privmsg(cmd->parent->bot, channel->name, response);
6888     }
6889     else
6890         send_message_type(4, user, cmd->parent->bot, "%s", resp);
6891     return 1;
6892 }
6893
6894 static CHANSERV_FUNC(cmd_d)
6895 {
6896     unsigned long sides, count, modifier, ii, total;
6897     char response[MAXLEN], *sep;
6898     const char *fmt;
6899
6900     REQUIRE_PARAMS(2);
6901     if((count = strtoul(argv[1], &sep, 10)) < 1)
6902         goto no_dice;
6903     if(sep[0] == 0)
6904     {
6905         if(count == 1)
6906             goto no_dice;
6907         sides = count;
6908         count = 1;
6909         modifier = 0;
6910     }
6911     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6912             && (sides = strtoul(sep+1, &sep, 10)) > 1)
6913     {
6914         if(sep[0] == 0)
6915             modifier = 0;
6916         else if((sep[0] == '-') && isdigit(sep[1]))
6917             modifier = strtoul(sep, NULL, 10);
6918         else if((sep[0] == '+') && isdigit(sep[1]))
6919             modifier = strtoul(sep+1, NULL, 10);
6920         else
6921             goto no_dice;
6922     }
6923     else
6924     {
6925       no_dice:
6926         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6927         return 0;
6928     }
6929     if(count > 10)
6930     {
6931         reply("CSMSG_BAD_DICE_COUNT", count, 10);
6932         return 0;
6933     }
6934     for(total = ii = 0; ii < count; ++ii)
6935         total += (rand() % sides) + 1;
6936     total += modifier;
6937
6938     if((count > 1) || modifier)
6939     {
6940         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6941         sprintf(response, fmt, total, count, sides, modifier);
6942     }
6943     else
6944     {
6945         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6946         sprintf(response, fmt, total, sides);
6947     }
6948     if(channel)
6949         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6950     else
6951         send_message_type(4, user, cmd->parent->bot, "%s", response);
6952     return 1;
6953 }
6954
6955 static CHANSERV_FUNC(cmd_huggle)
6956 {
6957     /* CTCP must be via PRIVMSG, never notice */
6958     if(channel)
6959         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6960     else
6961         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6962     return 1;
6963 }
6964
6965 static void
6966 chanserv_adjust_limit(void *data)
6967 {
6968     struct mod_chanmode change;
6969     struct chanData *cData = data;
6970     struct chanNode *channel = cData->channel;
6971     unsigned int limit;
6972
6973     if(IsSuspended(cData))
6974         return;
6975
6976     cData->limitAdjusted = now;
6977     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6978     if(cData->modes.modes_set & MODE_LIMIT)
6979     {
6980         if(limit > cData->modes.new_limit)
6981             limit = cData->modes.new_limit;
6982         else if(limit == cData->modes.new_limit)
6983             return;
6984     }
6985
6986     mod_chanmode_init(&change);
6987     change.modes_set = MODE_LIMIT;
6988     change.new_limit = limit;
6989     mod_chanmode_announce(chanserv, channel, &change);
6990 }
6991
6992 static void
6993 handle_new_channel(struct chanNode *channel)
6994 {
6995     struct chanData *cData;
6996
6997     if(!(cData = channel->channel_info))
6998         return;
6999
7000     if(cData->modes.modes_set || cData->modes.modes_clear)
7001         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7002
7003     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7004         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7005 }
7006
7007 /* Welcome to my worst nightmare. Warning: Read (or modify)
7008    the code below at your own risk. */
7009 static int
7010 handle_join(struct modeNode *mNode)
7011 {
7012     struct mod_chanmode change;
7013     struct userNode *user = mNode->user;
7014     struct chanNode *channel = mNode->channel;
7015     struct chanData *cData;
7016     struct userData *uData = NULL;
7017     struct banData *bData;
7018     struct handle_info *handle;
7019     unsigned int modes = 0, info = 0;
7020     char *greeting;
7021     unsigned int i = 0;
7022
7023     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7024         return 0;
7025
7026     cData = channel->channel_info;
7027     if(channel->members.used > cData->max) {
7028         cData->max = channel->members.used;
7029         cData->max_time = now;
7030     }
7031
7032     for(i = 0; i < channel->invited.used; i++)
7033     {
7034         if(channel->invited.list[i] == user) {
7035             userList_remove(&channel->invited, user);
7036         }
7037     }
7038
7039     /* Check for bans.  If they're joining through a ban, one of two
7040      * cases applies:
7041      *   1: Join during a netburst, by riding the break.  Kick them
7042      *      unless they have ops or voice in the channel.
7043      *   2: They're allowed to join through the ban (an invite in
7044      *   ircu2.10, or a +e on Hybrid, or something).
7045      * If they're not joining through a ban, and the banlist is not
7046      * full, see if they're on the banlist for the channel.  If so,
7047      * kickban them.
7048      */
7049     if(user->uplink->burst && !mNode->modes)
7050     {
7051         unsigned int ii;
7052         for(ii = 0; ii < channel->banlist.used; ii++)
7053         {
7054             if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7055             {
7056                 /* Riding a netburst.  Naughty. */
7057                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7058                 return 1;
7059             }
7060         }
7061     }
7062
7063     mod_chanmode_init(&change);
7064     change.argc = 1;
7065     if(channel->banlist.used < MAXBANS)
7066     {
7067         /* Not joining through a ban. */
7068         for(bData = cData->bans;
7069             bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7070             bData = bData->next);
7071
7072         if(bData)
7073         {
7074             char kick_reason[MAXLEN];
7075             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7076
7077             bData->triggered = now;
7078             if(bData != cData->bans)
7079             {
7080                 /* Shuffle the ban to the head of the list. */
7081                 if(bData->next)
7082                     bData->next->prev = bData->prev;
7083                 if(bData->prev)
7084                     bData->prev->next = bData->next;
7085
7086                 bData->prev = NULL;
7087                 bData->next = cData->bans;
7088
7089                 if(cData->bans)
7090                     cData->bans->prev = bData;
7091                 cData->bans = bData;
7092             }
7093
7094             change.args[0].mode = MODE_BAN;
7095             change.args[0].u.hostmask = bData->mask;
7096             mod_chanmode_announce(chanserv, channel, &change);
7097             KickChannelUser(user, channel, chanserv, kick_reason);
7098             return 1;
7099         }
7100     }
7101
7102     /* ChanServ will not modify the limits in join-flooded channels,
7103        or when there are enough slots left below the limit. */
7104     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7105        && !channel->join_flooded
7106        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7107     {
7108         /* The user count has begun "bumping" into the channel limit,
7109            so set a timer to raise the limit a bit. Any previous
7110            timers are removed so three incoming users within the delay
7111            results in one limit change, not three. */
7112
7113         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7114         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7115     }
7116
7117     if(channel->join_flooded)
7118     {
7119         /* don't automatically give ops or voice during a join flood */
7120     }
7121     else if(cData->lvlOpts[lvlGiveOps] == 0)
7122         modes |= MODE_CHANOP;
7123     else if(cData->lvlOpts[lvlGiveVoice] == 0)
7124         modes |= MODE_VOICE;
7125
7126     greeting = cData->greeting;
7127     if(user->handle_info)
7128     {
7129         handle = user->handle_info;
7130
7131         if(IsHelper(user) && !IsHelping(user))
7132         {
7133             unsigned int ii;
7134             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7135             {
7136                 if(channel == chanserv_conf.support_channels.list[ii])
7137                 {
7138                     HANDLE_SET_FLAG(user->handle_info, HELPING);
7139                     break;
7140                 }
7141             }
7142         }
7143
7144         uData = GetTrueChannelAccess(cData, handle);
7145         if(uData && !IsUserSuspended(uData))
7146         {
7147             /* Ops and above were handled by the above case. */
7148             if(IsUserAutoOp(uData))
7149             {
7150                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7151                     modes |= MODE_CHANOP;
7152                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7153                     modes |= MODE_VOICE;
7154             }
7155             if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7156                 cData->visited = now;
7157             if(cData->user_greeting)
7158                 greeting = cData->user_greeting;
7159             if(uData->info
7160                && (uData->access >= cData->lvlOpts[lvlUserInfo])
7161                && ((now - uData->seen) >= chanserv_conf.info_delay)
7162                && !uData->present)
7163                 info = 1;
7164             uData->seen = now;
7165             uData->present = 1;
7166         }
7167     }
7168
7169     /* If user joining normally (not during burst), apply op or voice,
7170      * and send greeting/userinfo as appropriate.
7171      */
7172     if(!user->uplink->burst)
7173     {
7174         if(modes)
7175         {
7176             if(modes & MODE_CHANOP)
7177                 modes &= ~MODE_VOICE;
7178             change.args[0].mode = modes;
7179             change.args[0].u.member = mNode;
7180             mod_chanmode_announce(chanserv, channel, &change);
7181         }
7182         if(greeting)
7183             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7184         if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7185             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7186     }
7187     return 0;
7188 }
7189
7190 static void
7191 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7192 {
7193     struct mod_chanmode change;
7194     struct userData *channel;
7195     unsigned int ii, jj;
7196
7197     if(!user->handle_info)
7198         return;
7199
7200     mod_chanmode_init(&change);
7201     change.argc = 1;
7202     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7203     {
7204         struct chanNode *cn;
7205         struct modeNode *mn;
7206         if(IsUserSuspended(channel)
7207            || IsSuspended(channel->channel)
7208            || !(cn = channel->channel->channel))
7209             continue;
7210
7211         mn = GetUserMode(cn, user);
7212         if(!mn)
7213         {
7214             if(!IsUserSuspended(channel)
7215                && IsUserAutoInvite(channel)
7216                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7217                && !self->burst
7218                && !user->uplink->burst)
7219                 irc_invite(chanserv, user, cn);
7220             continue;
7221         }
7222
7223         if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7224             channel->channel->visited = now;
7225
7226         if(IsUserAutoOp(channel))
7227         {
7228             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7229                 change.args[0].mode = MODE_CHANOP;
7230             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7231                 change.args[0].mode = MODE_VOICE;
7232             else
7233                 change.args[0].mode = 0;
7234             change.args[0].u.member = mn;
7235             if(change.args[0].mode)
7236                 mod_chanmode_announce(chanserv, cn, &change);
7237         }
7238
7239         channel->seen = now;
7240         channel->present = 1;
7241     }
7242
7243     for(ii = 0; ii < user->channels.used; ++ii)
7244     {
7245         struct chanNode *chan = user->channels.list[ii]->channel;
7246         struct banData *ban;
7247
7248         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7249            || !chan->channel_info
7250            || IsSuspended(chan->channel_info))
7251             continue;
7252         for(jj = 0; jj < chan->banlist.used; ++jj)
7253             if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7254                 break;
7255         if(jj < chan->banlist.used)
7256             continue;
7257         for(ban = chan->channel_info->bans; ban; ban = ban->next)
7258         {
7259             char kick_reason[MAXLEN];
7260             if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7261                 continue;
7262             change.args[0].mode = MODE_BAN;
7263             change.args[0].u.hostmask = ban->mask;
7264             mod_chanmode_announce(chanserv, chan, &change);
7265             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7266             KickChannelUser(user, chan, chanserv, kick_reason);
7267             ban->triggered = now;
7268             break;
7269         }
7270     }
7271
7272     if(IsSupportHelper(user))
7273     {
7274         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7275         {
7276             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7277             {
7278                 HANDLE_SET_FLAG(user->handle_info, HELPING);
7279                 break;
7280             }
7281         }
7282     }
7283 }
7284
7285 static void
7286 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7287 {
7288     struct chanData *cData;
7289     struct userData *uData;
7290
7291     cData = mn->channel->channel_info;
7292     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7293         return;
7294
7295     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7296     {
7297         /* Allow for a bit of padding so that the limit doesn't
7298            track the user count exactly, which could get annoying. */
7299         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7300         {
7301             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7302             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7303         }
7304     }
7305
7306     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7307     {
7308         scan_user_presence(uData, mn->user);
7309         uData->seen = now;
7310         if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7311             cData->visited = now;
7312     }
7313
7314     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7315     {
7316         unsigned int ii;
7317         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7318             struct chanNode *channel;
7319             struct userNode *exclude;
7320             /* When looking at the channel that is being /part'ed, we
7321              * have to skip over the client that is leaving.  For
7322              * other channels, we must not do that.
7323              */
7324             channel = chanserv_conf.support_channels.list[ii];
7325             exclude = (channel == mn->channel) ? mn->user : NULL;
7326             if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7327                 break;
7328         }
7329         if(ii == chanserv_conf.support_channels.used)
7330             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7331     }
7332 }
7333
7334 static void
7335 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7336 {
7337     struct userData *uData;
7338
7339     if(!channel->channel_info || !kicker || IsService(kicker)
7340        || (kicker == victim) || IsSuspended(channel->channel_info)
7341        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7342         return;
7343
7344     if(protect_user(victim, kicker, channel->channel_info))
7345     {
7346         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7347         KickChannelUser(kicker, channel, chanserv, reason);
7348     }
7349
7350     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7351         uData->seen = now;
7352 }
7353
7354 static int
7355 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7356 {
7357     struct chanData *cData;
7358
7359     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7360         return 0;
7361
7362     cData = channel->channel_info;
7363     if(bad_topic(channel, user, channel->topic))
7364     {
7365         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7366         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7367             SetChannelTopic(channel, chanserv, old_topic, 1);
7368         else if(cData->topic)
7369             SetChannelTopic(channel, chanserv, cData->topic, 1);
7370         return 1;
7371     }
7372     /* With topicsnarf, grab the topic and save it as the default topic. */
7373     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7374     {
7375         free(cData->topic);
7376         cData->topic = strdup(channel->topic);
7377     }
7378     return 0;
7379 }
7380
7381 static void
7382 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7383 {
7384     struct mod_chanmode *bounce = NULL;
7385     unsigned int bnc, ii;
7386     char deopped = 0;
7387
7388     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7389         return;
7390
7391     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7392        && mode_lock_violated(&channel->channel_info->modes, change))
7393     {
7394         char correct[MAXLEN];
7395         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7396         mod_chanmode_format(&channel->channel_info->modes, correct);
7397         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7398     }
7399     for(ii = bnc = 0; ii < change->argc; ++ii)
7400     {
7401         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7402         {
7403             const struct userNode *victim = change->args[ii].u.member->user;
7404             if(!protect_user(victim, user, channel->channel_info))
7405                 continue;
7406             if(!bounce)
7407                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7408             if(!deopped)
7409             {
7410                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7411                 bounce->args[bnc].u.member = GetUserMode(channel, user);
7412                 if(bounce->args[bnc].u.member)
7413                     bnc++;
7414                 deopped = 1;
7415             }
7416             bounce->args[bnc].mode = MODE_CHANOP;
7417             bounce->args[bnc].u.member = change->args[ii].u.member;
7418             bnc++;
7419             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7420         }
7421         else if(change->args[ii].mode & MODE_CHANOP)
7422         {
7423             const struct userNode *victim = change->args[ii].u.member->user;
7424             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7425                 continue;
7426             if(!bounce)
7427                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7428             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7429             bounce->args[bnc].u.member = change->args[ii].u.member;
7430             bnc++;
7431         }
7432         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7433         {
7434             const char *ban = change->args[ii].u.hostmask;
7435             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7436                 continue;
7437             if(!bounce)
7438                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7439             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7440             bounce->args[bnc].u.hostmask = strdup(ban);
7441             bnc++;
7442             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7443         }
7444     }
7445     if(bounce)
7446     {
7447         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7448             mod_chanmode_announce(chanserv, channel, bounce);
7449         for(ii = 0; ii < change->argc; ++ii)
7450             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7451                 free((char*)bounce->args[ii].u.hostmask);
7452         mod_chanmode_free(bounce);
7453     }
7454 }
7455
7456 static void
7457 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7458 {
7459     struct chanNode *channel;
7460     struct banData *bData;
7461     struct mod_chanmode change;
7462     unsigned int ii, jj;
7463     char kick_reason[MAXLEN];
7464
7465     mod_chanmode_init(&change);
7466     change.argc = 1;
7467     change.args[0].mode = MODE_BAN;
7468     for(ii = 0; ii < user->channels.used; ++ii)
7469     {
7470         channel = user->channels.list[ii]->channel;
7471         /* Need not check for bans if they're opped or voiced. */
7472         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7473             continue;
7474         /* Need not check for bans unless channel registration is active. */
7475         if(!channel->channel_info || IsSuspended(channel->channel_info))
7476             continue;
7477         /* Look for a matching ban already on the channel. */
7478         for(jj = 0; jj < channel->banlist.used; ++jj)
7479             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7480                 break;
7481         /* Need not act if we found one. */
7482         if(jj < channel->banlist.used)
7483             continue;
7484         /* Look for a matching ban in this channel. */
7485         for(bData = channel->channel_info->bans; bData; bData = bData->next)
7486         {
7487             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7488                 continue;
7489             change.args[0].u.hostmask = bData->mask;
7490             mod_chanmode_announce(chanserv, channel, &change);
7491             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7492             KickChannelUser(user, channel, chanserv, kick_reason);
7493             bData->triggered = now;
7494             break; /* we don't need to check any more bans in the channel */
7495         }
7496     }
7497 }
7498
7499 static void handle_rename(struct handle_info *handle, const char *old_handle)
7500 {
7501     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7502
7503     if(dnr)
7504     {
7505         dict_remove2(handle_dnrs, old_handle, 1);
7506         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7507         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7508     }
7509 }
7510
7511 static void
7512 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7513 {
7514     struct userNode *h_user;
7515
7516     if(handle->channels)
7517     {
7518         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7519             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7520
7521         while(handle->channels)
7522             del_channel_user(handle->channels, 1);
7523     }
7524 }
7525
7526 static void
7527 handle_server_link(UNUSED_ARG(struct server *server))
7528 {
7529     struct chanData *cData;
7530
7531     for(cData = channelList; cData; cData = cData->next)
7532     {
7533         if(!IsSuspended(cData))
7534             cData->may_opchan = 1;
7535         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7536            && !cData->channel->join_flooded
7537            && ((cData->channel->limit - cData->channel->members.used)
7538                < chanserv_conf.adjust_threshold))
7539         {
7540             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7541             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7542         }
7543     }
7544 }
7545
7546 static void
7547 chanserv_conf_read(void)
7548 {
7549     dict_t conf_node;
7550     const char *str;
7551     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7552     struct mod_chanmode *change;
7553     struct string_list *strlist;
7554     struct chanNode *chan;
7555     unsigned int ii;
7556
7557     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7558     {
7559         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7560         return;
7561     }
7562     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7563         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7564     chanserv_conf.support_channels.used = 0;
7565     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7566     {
7567         for(ii = 0; ii < strlist->used; ++ii)
7568         {
7569             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7570             if(!str2)
7571                 str2 = "+nt";
7572             chan = AddChannel(strlist->list[ii], now, str2, NULL);
7573             LockChannel(chan);
7574             channelList_append(&chanserv_conf.support_channels, chan);
7575         }
7576     }
7577     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7578     {
7579         const char *str2;
7580         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7581         if(!str2)
7582             str2 = "+nt";
7583         chan = AddChannel(str, now, str2, NULL);
7584         LockChannel(chan);
7585         channelList_append(&chanserv_conf.support_channels, chan);
7586     }
7587     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7588     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7589     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7590     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7591     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7592     chanserv_conf.greeting_length = str ? atoi(str) : 200;
7593     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7594     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7595     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7596     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7597     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7598     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7599     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7600     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7601     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7602     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7603     str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7604     chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7605     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7606     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7607     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7608     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7609     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7610     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7611     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7612     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7613     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7614     if(chanserv && str)
7615         NickChange(chanserv, str, 0);
7616     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7617     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7618     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7619     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7620     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7621     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7622     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7623     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7624     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7625     chanserv_conf.max_owned = str ? atoi(str) : 5;
7626     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7627     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7628     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7629     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7630     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7631     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7632     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7633     if(!str)
7634         str = "+nt";
7635     safestrncpy(mode_line, str, sizeof(mode_line));
7636     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7637     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7638        && (change->argc < 2))
7639     {
7640         chanserv_conf.default_modes = *change;
7641         mod_chanmode_free(change);
7642     }
7643     free_string_list(chanserv_conf.set_shows);
7644     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7645     if(strlist)
7646         strlist = string_list_copy(strlist);
7647     else
7648     {
7649         static const char *list[] = {
7650             /* free form text */
7651             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7652             /* options based on user level */
7653             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7654             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7655             /* multiple choice options */
7656             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7657             /* binary options */
7658             "DynLimit", "NoDelete", "expire", "Vote",
7659             /* delimiter */
7660             NULL
7661         };
7662         strlist = alloc_string_list(ArrayLength(list)-1);
7663         for(ii=0; list[ii]; ii++)
7664             string_list_append(strlist, strdup(list[ii]));
7665     }
7666     chanserv_conf.set_shows = strlist;
7667     /* We don't look things up now, in case the list refers to options
7668      * defined by modules initialized after this point.  Just mark the
7669      * function list as invalid, so it will be initialized.
7670      */
7671     set_shows_list.used = 0;
7672     free_string_list(chanserv_conf.eightball);
7673     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7674     if(strlist)
7675     {
7676         strlist = string_list_copy(strlist);
7677     }
7678     else
7679     {
7680         strlist = alloc_string_list(4);
7681         string_list_append(strlist, strdup("Yes."));
7682         string_list_append(strlist, strdup("No."));
7683         string_list_append(strlist, strdup("Maybe so."));
7684     }
7685     chanserv_conf.eightball = strlist;
7686     free_string_list(chanserv_conf.old_ban_names);
7687     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7688     if(strlist)
7689         strlist = string_list_copy(strlist);
7690     else
7691         strlist = alloc_string_list(2);
7692     chanserv_conf.old_ban_names = strlist;
7693     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7694     off_channel = str ? atoi(str) : 0;
7695 }
7696
7697 static void
7698 chanserv_note_type_read(const char *key, struct record_data *rd)
7699 {
7700     dict_t obj;
7701     struct note_type *ntype;
7702     const char *str;
7703
7704     if(!(obj = GET_RECORD_OBJECT(rd)))
7705     {
7706         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7707         return;
7708     }
7709     if(!(ntype = chanserv_create_note_type(key)))
7710     {
7711         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7712         return;
7713     }
7714
7715     /* Figure out set access */
7716     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7717     {
7718         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7719         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7720     }
7721     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7722     {
7723         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7724         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7725     }
7726     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7727     {
7728         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7729     }
7730     else
7731     {
7732         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7733         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7734         ntype->set_access.min_opserv = 0;
7735     }
7736
7737     /* Figure out visibility */
7738     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7739         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7740     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7741         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7742     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7743         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7744     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7745         ntype->visible_type = NOTE_VIS_ALL;
7746     else
7747         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7748
7749     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7750     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7751 }
7752
7753 static void
7754 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7755 {
7756     struct vote_option *vOpt;
7757     char *str;
7758     
7759     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7760     {
7761         log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7762         return;
7763     }
7764     
7765     vOpt = calloc(1, sizeof(*vOpt));
7766     vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7767     str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7768     vOpt->voted = str ? atoi(str) : 0;
7769     vOpt->option_id = str ? atoi(key) : 0;
7770     vOpt->option_str = strdup(key);
7771     dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7772 }
7773
7774 static void
7775 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7776 {
7777     struct handle_info *handle;
7778     struct userData *uData;
7779     char *seen, *inf, *flags, *voted, *votefor;
7780     unsigned long last_seen;
7781     unsigned short access_level;
7782
7783     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7784     {
7785         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7786         return;
7787     }
7788
7789     access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7790     if(access_level > UL_OWNER)
7791     {
7792         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7793         return;
7794     }
7795
7796     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7797     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7798     last_seen = seen ? strtoul(seen, NULL, 0) : now;
7799     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7800     voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7801     votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7802     handle = get_handle_info(key);
7803     if(!handle)
7804     {
7805         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7806         return;
7807     }
7808
7809     uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7810     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7811     if(chan->vote) {
7812         uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7813         uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7814     } else {
7815         uData->voted = 0;
7816         uData->votefor = 0;
7817     }
7818 }
7819
7820 static void
7821 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7822 {
7823     struct banData *bData;
7824     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7825     unsigned long set_time, triggered_time, expires_time;
7826
7827     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7828     {
7829         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7830         return;
7831     }
7832
7833     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7834     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7835     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7836     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7837     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7838     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7839     if (!reason || !owner)
7840         return;
7841
7842     set_time = set ? strtoul(set, NULL, 0) : now;
7843     triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7844     if(s_expires)
7845         expires_time = strtoul(s_expires, NULL, 0);
7846     else if(s_duration)
7847         expires_time = set_time + atoi(s_duration);
7848     else
7849         expires_time = 0;
7850
7851     if(!reason || (expires_time && (expires_time < now)))
7852         return;
7853
7854     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7855 }
7856
7857 static struct suspended *
7858 chanserv_read_suspended(dict_t obj)
7859 {
7860     struct suspended *suspended = calloc(1, sizeof(*suspended));
7861     char *str;
7862     dict_t previous;
7863
7864     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7865     suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7866     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7867     suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7868     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7869     suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7870     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7871     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7872     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7873     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7874     return suspended;
7875 }
7876
7877 static int
7878 chanserv_channel_read(const char *key, struct record_data *hir)
7879 {
7880     struct suspended *suspended;
7881     struct mod_chanmode *modes;
7882     struct chanNode *cNode;
7883     struct chanData *cData;
7884     struct dict *channel, *obj;
7885     char *str, *argv[10];
7886     dict_iterator_t it;
7887     unsigned int argc;
7888
7889     channel = hir->d.object;
7890
7891     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7892     if(!str)
7893         str = "<unknown>";
7894     cNode = AddChannel(key, now, NULL, NULL);
7895     if(!cNode)
7896     {
7897         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7898         return 0;
7899     }
7900     cData = register_channel(cNode, str);
7901     if(!cData)
7902     {
7903         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7904         return 0;
7905     }
7906
7907     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7908     {
7909         enum levelOption lvlOpt;
7910         enum charOption chOpt;
7911
7912         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7913             cData->flags = atoi(str);
7914
7915         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7916         {
7917             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7918             if(str)
7919                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7920             else if(levelOptions[lvlOpt].old_flag)
7921             {
7922                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7923                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7924                 else
7925                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7926             }
7927         }
7928
7929         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7930         {
7931             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7932                 continue;
7933             cData->chOpts[chOpt] = str[0];
7934         }
7935     }
7936     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7937     {
7938         enum levelOption lvlOpt;
7939         enum charOption chOpt;
7940         unsigned int count;
7941
7942         cData->flags = base64toint(str, 5);
7943         count = strlen(str += 5);
7944         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7945         {
7946             unsigned short lvl;
7947             if(levelOptions[lvlOpt].old_flag)
7948             {
7949                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7950                     lvl = levelOptions[lvlOpt].flag_value;
7951                 else
7952                     lvl = levelOptions[lvlOpt].default_value;
7953             }
7954             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7955             {
7956             case 'c': lvl = UL_COOWNER; break;
7957             case 'm': lvl = UL_MASTER; break;
7958             case 'n': lvl = UL_OWNER+1; break;
7959             case 'o': lvl = UL_OP; break;
7960             case 'p': lvl = UL_PEON; break;
7961             case 'w': lvl = UL_OWNER; break;
7962             default: lvl = 0; break;
7963             }
7964             cData->lvlOpts[lvlOpt] = lvl;
7965         }
7966         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7967             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7968     }
7969
7970     if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
7971     {
7972         cData->expiry = atoi(str);
7973         if(cData->expiry > 0) {
7974             if(cData->expiry > now) {
7975                 timeq_add(cData->expiry, chanserv_expire_channel, cData);
7976             } else {
7977                 timeq_add(1, chanserv_expire_channel, cData);
7978             }
7979         }
7980     } else {
7981         cData->expiry = 0;
7982     }
7983
7984     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7985     {
7986         suspended = chanserv_read_suspended(obj);
7987         cData->suspended = suspended;
7988         suspended->cData = cData;
7989         /* We could use suspended->expires and suspended->revoked to
7990          * set the CHANNEL_SUSPENDED flag, but we don't. */
7991     }
7992     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7993     {
7994         suspended = calloc(1, sizeof(*suspended));
7995         suspended->issued = 0;
7996         suspended->revoked = 0;
7997         suspended->suspender = strdup(str);
7998         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7999         suspended->expires = str ? atoi(str) : 0;
8000         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8001         suspended->reason = strdup(str ? str : "No reason");
8002         suspended->previous = NULL;
8003         cData->suspended = suspended;
8004         suspended->cData = cData;
8005     }
8006     else
8007     {
8008         cData->flags &= ~CHANNEL_SUSPENDED;
8009         suspended = NULL; /* to squelch a warning */
8010     }
8011
8012     if(IsSuspended(cData)) {
8013         if(suspended->expires > now)
8014             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8015         else if(suspended->expires)
8016             cData->flags &= ~CHANNEL_SUSPENDED;
8017     }
8018
8019     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8020         struct mod_chanmode change;
8021         mod_chanmode_init(&change);
8022         change.argc = 1;
8023         change.args[0].mode = MODE_CHANOP;
8024         change.args[0].u.member = AddChannelUser(chanserv, cNode);
8025         mod_chanmode_announce(chanserv, cNode, &change);
8026     }
8027
8028     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8029     cData->registered = str ? strtoul(str, NULL, 0) : now;
8030     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8031     cData->visited = str ? strtoul(str, NULL, 0) : now;
8032     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8033     cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8034     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8035     cData->max = str ? atoi(str) : 0;
8036     str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8037     cData->max_time = str ? atoi(str) : 0;
8038     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8039     cData->greeting = str ? strdup(str) : NULL;
8040     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8041     cData->user_greeting = str ? strdup(str) : NULL;
8042     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8043     cData->topic_mask = str ? strdup(str) : NULL;
8044     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8045     cData->topic = str ? strdup(str) : NULL;
8046
8047     str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8048     if(str) {
8049         cData->vote = str ? strdup(str) : NULL;
8050         dict_delete(cData->vote_options);
8051         cData->vote_options = dict_new();
8052         dict_set_free_data(cData->vote_options, free_vote_options);
8053         str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8054         cData->vote_start = str ? atoi(str) : 0;
8055         obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8056         for(it = dict_first(obj); it; it = iter_next(it)) {
8057             vote_option_read_helper(iter_key(it), iter_data(it), cData);
8058         }
8059     }
8060
8061     if(!IsSuspended(cData)
8062        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8063        && (argc = split_line(str, 0, ArrayLength(argv), argv))
8064        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8065         cData->modes = *modes;
8066         if(off_channel > 0)
8067           cData->modes.modes_set |= MODE_REGISTERED;
8068         if(cData->modes.argc > 1)
8069             cData->modes.argc = 1;
8070         mod_chanmode_announce(chanserv, cNode, &cData->modes);
8071         mod_chanmode_free(modes);
8072     }
8073
8074     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8075     for(it = dict_first(obj); it; it = iter_next(it))
8076         user_read_helper(iter_key(it), iter_data(it), cData);
8077
8078     if(!cData->users && !IsProtected(cData))
8079     {
8080         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8081         unregister_channel(cData, "has empty user list.");
8082         return 0;
8083     }
8084
8085     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8086     for(it = dict_first(obj); it; it = iter_next(it))
8087         ban_read_helper(iter_key(it), iter_data(it), cData);
8088
8089     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8090     for(it = dict_first(obj); it; it = iter_next(it))
8091     {
8092         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8093         struct record_data *rd = iter_data(it);
8094         const char *note, *setter;
8095
8096         if(rd->type != RECDB_OBJECT)
8097         {
8098             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8099         }
8100         else if(!ntype)
8101         {
8102             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8103         }
8104         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8105         {
8106             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8107         }
8108         else
8109         {
8110             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8111             if(!setter) setter = "<unknown>";
8112             chanserv_add_channel_note(cData, ntype, setter, note);
8113         }
8114     }
8115
8116     return 0;
8117 }
8118
8119 static void
8120 chanserv_dnr_read(const char *key, struct record_data *hir)
8121 {
8122     const char *setter, *reason, *str;
8123     struct do_not_register *dnr;
8124     unsigned long expiry;
8125
8126     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8127     if(!setter)
8128     {
8129         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8130         return;
8131     }
8132     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8133     if(!reason)
8134     {
8135         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8136         return;
8137     }
8138     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8139     expiry = str ? strtoul(str, NULL, 0) : 0;
8140     if(expiry && expiry <= now)
8141         return;
8142     dnr = chanserv_add_dnr(key, setter, expiry, reason);
8143     if(!dnr)
8144         return;
8145     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8146     if(str)
8147         dnr->set = atoi(str);
8148     else
8149         dnr->set = 0;
8150 }
8151
8152 static int
8153 chanserv_saxdb_read(struct dict *database)
8154 {
8155     struct dict *section;
8156     dict_iterator_t it;
8157
8158     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8159         for(it = dict_first(section); it; it = iter_next(it))
8160             chanserv_note_type_read(iter_key(it), iter_data(it));
8161
8162     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8163         for(it = dict_first(section); it; it = iter_next(it))
8164             chanserv_channel_read(iter_key(it), iter_data(it));
8165
8166     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8167         for(it = dict_first(section); it; it = iter_next(it))
8168             chanserv_dnr_read(iter_key(it), iter_data(it));
8169
8170     return 0;
8171 }
8172
8173 static int
8174 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8175 {
8176     int high_present = 0;
8177     saxdb_start_record(ctx, KEY_USERS, 1);
8178     for(; uData; uData = uData->next)
8179     {
8180         if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8181             high_present = 1;
8182         saxdb_start_record(ctx, uData->handle->handle, 0);
8183         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8184         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8185         if(uData->flags)
8186             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8187         if(uData->channel->vote && uData->voted)
8188             saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8189         if(uData->channel->vote && uData->votefor)
8190             saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8191         if(uData->info)
8192             saxdb_write_string(ctx, KEY_INFO, uData->info);
8193         saxdb_end_record(ctx);
8194     }
8195     saxdb_end_record(ctx);
8196     return high_present;
8197 }
8198
8199 static void
8200 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8201 {
8202     if(!bData)
8203         return;
8204     saxdb_start_record(ctx, KEY_BANS, 1);
8205     for(; bData; bData = bData->next)
8206     {
8207         saxdb_start_record(ctx, bData->mask, 0);
8208         saxdb_write_int(ctx, KEY_SET, bData->set);
8209         if(bData->triggered)
8210             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8211         if(bData->expires)
8212             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8213         if(bData->owner[0])
8214             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8215         if(bData->reason)
8216             saxdb_write_string(ctx, KEY_REASON, bData->reason);
8217         saxdb_end_record(ctx);
8218     }
8219     saxdb_end_record(ctx);
8220 }
8221
8222 static void
8223 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8224 {
8225     saxdb_start_record(ctx, name, 0);
8226     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8227     saxdb_write_string(ctx, KEY_REASON, susp->reason);
8228     if(susp->issued)
8229         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8230     if(susp->expires)
8231         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8232     if(susp->revoked)
8233         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8234     if(susp->previous)
8235         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8236     saxdb_end_record(ctx);
8237 }
8238
8239 static void
8240 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8241 {
8242     char buf[MAXLEN];
8243     int high_present;
8244     enum levelOption lvlOpt;
8245     enum charOption chOpt;
8246     dict_iterator_t it;
8247
8248     saxdb_start_record(ctx, channel->channel->name, 1);
8249
8250     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8251     saxdb_write_int(ctx, KEY_MAX, channel->max);
8252     saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8253     if(channel->topic)
8254         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8255     if(channel->registrar)
8256         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8257     if(channel->greeting)
8258         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8259     if(channel->user_greeting)
8260         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8261     if(channel->topic_mask)
8262         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8263     if(channel->suspended)
8264         chanserv_write_suspended(ctx, "suspended", channel->suspended);
8265     if(channel->expiry)
8266         saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8267
8268     if(channel->vote) {
8269         saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8270         if(channel->vote_start)
8271             saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8272         if (dict_size(channel->vote_options)) {
8273             saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8274             for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8275                 struct vote_option *vOpt = iter_data(it);
8276                 char str[50];
8277                 sprintf(str,"%i",vOpt->option_id);
8278                 saxdb_start_record(ctx, str, 0);
8279                 if(vOpt->voted)
8280                     saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8281                 if(vOpt->name)
8282                     saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8283                 saxdb_end_record(ctx);
8284             }
8285             saxdb_end_record(ctx);
8286         }
8287     }
8288
8289     saxdb_start_record(ctx, KEY_OPTIONS, 0);
8290     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8291     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8292         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8293     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8294     {
8295         buf[0] = channel->chOpts[chOpt];
8296         buf[1] = '\0';
8297         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8298     }
8299     saxdb_end_record(ctx);
8300
8301     if(channel->modes.modes_set || channel->modes.modes_clear)
8302     {
8303         mod_chanmode_format(&channel->modes, buf);
8304         saxdb_write_string(ctx, KEY_MODES, buf);
8305     }
8306
8307     high_present = chanserv_write_users(ctx, channel->users);
8308     chanserv_write_bans(ctx, channel->bans);
8309
8310     if(dict_size(channel->notes))
8311     {
8312         dict_iterator_t it;
8313
8314         saxdb_start_record(ctx, KEY_NOTES, 1);
8315         for(it = dict_first(channel->notes); it; it = iter_next(it))
8316         {
8317             struct note *note = iter_data(it);
8318             saxdb_start_record(ctx, iter_key(it), 0);
8319             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8320             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8321             saxdb_end_record(ctx);
8322         }
8323         saxdb_end_record(ctx);
8324     }
8325
8326     if(channel->ownerTransfer)
8327         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8328     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8329     saxdb_end_record(ctx);
8330 }
8331
8332 static void
8333 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8334 {
8335     const char *str;
8336
8337     saxdb_start_record(ctx, ntype->name, 0);
8338     switch(ntype->set_access_type)
8339     {
8340     case NOTE_SET_CHANNEL_ACCESS:
8341         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8342         break;
8343     case NOTE_SET_CHANNEL_SETTER:
8344         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8345         break;
8346     case NOTE_SET_PRIVILEGED: default:
8347         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8348         break;
8349     }
8350     switch(ntype->visible_type)
8351     {
8352     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8353     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8354     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8355     }
8356     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8357     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8358     saxdb_end_record(ctx);
8359 }
8360
8361 static void
8362 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8363 {
8364     struct do_not_register *dnr;
8365     dict_iterator_t it, next;
8366
8367     for(it = dict_first(dnrs); it; it = next)
8368     {
8369         next = iter_next(it);
8370         dnr = iter_data(it);
8371         if(dnr->expires && dnr->expires <= now)
8372         {
8373             dict_remove(dnrs, iter_key(it));
8374             continue;
8375         }
8376         saxdb_start_record(ctx, dnr->chan_name, 0);
8377         if(dnr->set)
8378             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8379         if(dnr->expires)
8380             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8381         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8382         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8383         saxdb_end_record(ctx);
8384     }
8385 }
8386
8387 static int
8388 chanserv_saxdb_write(struct saxdb_context *ctx)
8389 {
8390     dict_iterator_t it;
8391     struct chanData *channel;
8392
8393     /* Notes */
8394     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8395     for(it = dict_first(note_types); it; it = iter_next(it))
8396         chanserv_write_note_type(ctx, iter_data(it));
8397     saxdb_end_record(ctx);
8398
8399     /* DNRs */
8400     saxdb_start_record(ctx, KEY_DNR, 1);
8401     write_dnrs_helper(ctx, handle_dnrs);
8402     write_dnrs_helper(ctx, plain_dnrs);
8403     write_dnrs_helper(ctx, mask_dnrs);
8404     saxdb_end_record(ctx);
8405
8406     /* Channels */
8407     saxdb_start_record(ctx, KEY_CHANNELS, 1);
8408     for(channel = channelList; channel; channel = channel->next)
8409         chanserv_write_channel(ctx, channel);
8410     saxdb_end_record(ctx);
8411
8412     return 0;
8413 }
8414
8415 static void
8416 chanserv_db_cleanup(void) {
8417     unsigned int ii;
8418     unreg_part_func(handle_part);
8419     while(channelList)
8420         unregister_channel(channelList, "terminating.");
8421     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8422         UnlockChannel(chanserv_conf.support_channels.list[ii]);
8423     free(chanserv_conf.support_channels.list);
8424     dict_delete(handle_dnrs);
8425     dict_delete(plain_dnrs);
8426     dict_delete(mask_dnrs);
8427     dict_delete(note_types);
8428     free_string_list(chanserv_conf.eightball);
8429     free_string_list(chanserv_conf.old_ban_names);
8430     free_string_list(chanserv_conf.set_shows);
8431     free(set_shows_list.list);
8432     free(uset_shows_list.list);
8433     while(helperList)
8434     {
8435         struct userData *helper = helperList;
8436         helperList = helperList->next;
8437         free(helper);
8438     }
8439 }
8440
8441 #if defined(GCC_VARMACROS)
8442 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8443 #elif defined(C99_VARMACROS)
8444 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8445 #endif
8446 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8447 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8448
8449 void
8450 init_chanserv(const char *nick)
8451 {
8452     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8453     conf_register_reload(chanserv_conf_read);
8454
8455     if(nick)
8456     {
8457         reg_server_link_func(handle_server_link);
8458         reg_new_channel_func(handle_new_channel);
8459         reg_join_func(handle_join);
8460         reg_part_func(handle_part);
8461         reg_kick_func(handle_kick);
8462         reg_topic_func(handle_topic);
8463         reg_mode_change_func(handle_mode);
8464         reg_nick_change_func(handle_nick_change);
8465         reg_auth_func(handle_auth);
8466     }
8467
8468     reg_handle_rename_func(handle_rename);
8469     reg_unreg_func(handle_unreg);
8470
8471     handle_dnrs = dict_new();
8472     dict_set_free_data(handle_dnrs, free);
8473     plain_dnrs = dict_new();
8474     dict_set_free_data(plain_dnrs, free);
8475     mask_dnrs = dict_new();
8476     dict_set_free_data(mask_dnrs, free);
8477
8478     reg_svccmd_unbind_func(handle_svccmd_unbind);
8479     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8480     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8481     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8482     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8483     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8484     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8485     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8486     modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8487     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8488     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8489     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8490     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8491     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8492
8493     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8494     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8495
8496     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8497     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8498     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8499     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8500     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8501
8502     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8503     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8504     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8505     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8506     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8507
8508     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8509     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8510     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8511     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8512
8513     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8514     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8515     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8516     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8517     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8518     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8519     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8520     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8521
8522     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8523     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8524     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8525     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8526     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8527     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8528     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8529     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8530     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8531     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8532     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8533     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8534     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8535     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8536
8537     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8538     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8539     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8540     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8541     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8542
8543     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8544     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8545
8546     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8547     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8548     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8549     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8550     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8551     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8552     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8553     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8554     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8555     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8556     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8557
8558     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8559     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8560
8561     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8562     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8563     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8564     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8565
8566     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8567     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8568     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8569     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8570     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8571
8572     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8573     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8574     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8575     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8576     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8577     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8578     
8579     DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8580     DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8581     DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8582     DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8583     DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8584     DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8585     DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8586     DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8587
8588     /* Channel options */
8589     DEFINE_CHANNEL_OPTION(defaulttopic);
8590     DEFINE_CHANNEL_OPTION(topicmask);
8591     DEFINE_CHANNEL_OPTION(greeting);
8592     DEFINE_CHANNEL_OPTION(usergreeting);
8593     DEFINE_CHANNEL_OPTION(modes);
8594     DEFINE_CHANNEL_OPTION(enfops);
8595     DEFINE_CHANNEL_OPTION(giveops);
8596     DEFINE_CHANNEL_OPTION(protect);
8597     DEFINE_CHANNEL_OPTION(enfmodes);
8598     DEFINE_CHANNEL_OPTION(enftopic);
8599     DEFINE_CHANNEL_OPTION(pubcmd);
8600     DEFINE_CHANNEL_OPTION(givevoice);
8601     DEFINE_CHANNEL_OPTION(userinfo);
8602     DEFINE_CHANNEL_OPTION(dynlimit);
8603     DEFINE_CHANNEL_OPTION(topicsnarf);
8604     DEFINE_CHANNEL_OPTION(vote);
8605     DEFINE_CHANNEL_OPTION(nodelete);
8606     DEFINE_CHANNEL_OPTION(toys);
8607     DEFINE_CHANNEL_OPTION(setters);
8608     DEFINE_CHANNEL_OPTION(topicrefresh);
8609     DEFINE_CHANNEL_OPTION(ctcpusers);
8610     DEFINE_CHANNEL_OPTION(ctcpreaction);
8611     DEFINE_CHANNEL_OPTION(inviteme);
8612     DEFINE_CHANNEL_OPTION(unreviewed);
8613     modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8614     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8615     modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8616     if(off_channel > 1)
8617         DEFINE_CHANNEL_OPTION(offchannel);
8618     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8619
8620     /* Alias set topic to set defaulttopic for compatibility. */
8621     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8622
8623     /* User options */
8624     DEFINE_USER_OPTION(noautoop);
8625     DEFINE_USER_OPTION(autoinvite);
8626     DEFINE_USER_OPTION(info);
8627
8628     /* Alias uset autovoice to uset autoop. */
8629     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8630
8631     note_types = dict_new();
8632     dict_set_free_data(note_types, chanserv_deref_note_type);
8633     if(nick)
8634     {
8635         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8636         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8637         service_register(chanserv)->trigger = '!';
8638         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8639     }
8640     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8641
8642     if(chanserv_conf.channel_expire_frequency)
8643         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8644
8645     if(chanserv_conf.dnr_expire_frequency)
8646         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8647
8648     if(chanserv_conf.refresh_period)
8649     {
8650         unsigned long next_refresh;
8651         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8652         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8653     }
8654
8655     reg_exit_func(chanserv_db_cleanup);
8656     message_register_table(msgtab);
8657 }