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