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