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