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