1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
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.
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.
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.
25 #include "opserv.h" /* for opserv_bad_channel() and devnull management */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
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"
66 /* ChanServ database */
67 #define KEY_CHANNELS "channels"
68 #define KEY_NOTE_TYPES "note_types"
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"
82 /* Do-not-register channels */
84 #define KEY_DNR_SET "set"
85 #define KEY_DNR_SETTER "setter"
86 #define KEY_DNR_REASON "reason"
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_ADVTOPIC_ENTRIES "adv_topic"
117 #define KEY_OWNER_TRANSFER "owner_transfer"
118 #define KEY_EXPIRE "expire"
121 #define KEY_LEVEL "level"
122 #define KEY_INFO "info"
123 #define KEY_SEEN "seen"
126 #define KEY_VOTE "vote"
127 #define KEY_VOTE_START "votestart"
128 #define KEY_VOTE_OPTIONS "voptions"
129 #define KEY_VOTE_OPTION_NAME "voptionname"
130 #define KEY_VOTE_VOTED "vvoted"
131 #define KEY_VOTE_VOTEDFOR "vvotefor"
132 #define KEY_VOTE_OPTION_ID "voptionid"
133 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
136 #define KEY_OWNER "owner"
137 #define KEY_REASON "reason"
138 #define KEY_SET "set"
139 #define KEY_DURATION "duration"
140 #define KEY_EXPIRES "expires"
141 #define KEY_TRIGGERED "triggered"
143 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
144 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
145 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
147 /* Administrative messages */
148 static const struct message_entry msgtab[] = {
149 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
151 /* Channel registration */
152 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
153 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
154 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
155 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
156 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
157 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
159 /* Do-not-register channels */
160 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
161 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
162 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
163 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
164 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
165 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
166 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
167 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
168 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
169 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
170 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
171 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
172 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
173 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
175 /* Channel unregistration */
176 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
177 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
178 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
179 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
182 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
183 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
185 /* Channel merging */
186 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
187 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
188 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
189 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
190 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
192 /* Handle unregistration */
193 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
196 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
197 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
198 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
199 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
200 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
201 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
202 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
203 { "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." },
204 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
205 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
206 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
207 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
208 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
209 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
211 /* Removing yourself from a channel. */
212 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
213 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
214 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
216 /* User management */
217 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
218 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
219 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
220 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
221 { "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." },
222 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
223 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
224 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
226 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
227 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
228 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
229 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
230 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
231 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
232 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
235 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
236 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
237 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
238 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
239 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
240 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
241 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
242 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
243 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
244 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
245 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
246 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
247 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
248 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
249 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
250 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
252 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
254 /* Channel management */
255 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
256 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
257 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
259 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
260 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
261 { "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" },
262 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
263 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
264 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
265 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
266 { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
268 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
269 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
270 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
271 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
272 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
273 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
274 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
275 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
276 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
277 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
278 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
279 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
280 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
281 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
282 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
283 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
284 { "CSMSG_SET_ADVTOPIC", "$bAdvTopic $b %s" },
285 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
286 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
287 { "CSMSG_SET_MODES", "$bModes $b %s" },
288 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
289 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
290 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
291 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
292 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
293 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
294 { "CSMSG_SET_VOTE", "$bVote $b %d" },
295 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
296 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
297 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
298 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
299 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
300 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
301 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
302 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
303 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
304 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
305 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
306 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
307 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
308 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
309 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
310 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
311 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
312 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
313 { "CSMSG_USET_INFO", "$bInfo $b %s" },
315 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
316 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
317 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
318 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
319 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
320 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
321 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
322 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
323 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
324 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
325 { "CSMSG_PROTECT_NONE", "No users will be protected." },
326 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
327 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
328 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
329 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
330 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
331 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
332 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
333 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
334 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
335 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
336 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
337 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
339 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
340 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
341 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
342 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
343 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
344 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
345 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
346 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
348 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
349 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
350 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
352 /* Channel userlist */
353 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
354 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
355 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
356 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
357 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
359 /* Channel note list */
360 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
361 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
362 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
363 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
364 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
365 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
366 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
367 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
368 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
369 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
370 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
371 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
372 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
373 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
374 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
376 /* Channel [un]suspension */
377 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
378 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
379 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
380 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
381 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
382 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
383 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
385 /* Access information */
386 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
387 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
388 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
389 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
390 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
391 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
392 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
393 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
394 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
395 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
396 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
397 { "CSMSG_UC_H_TITLE", "network helper" },
398 { "CSMSG_LC_H_TITLE", "support helper" },
399 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
400 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
401 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
404 /* Seen information */
405 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
406 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
407 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
408 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
410 /* Names information */
411 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
412 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
414 /* Channel information */
415 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
416 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
417 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
418 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
419 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
420 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
421 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
422 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
423 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
424 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
425 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
426 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
427 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
428 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
435 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
436 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
438 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
439 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
440 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
441 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
443 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
444 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
445 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
446 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
447 { "CSMSG_PEEK_OPS", "$bOps:$b" },
448 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
450 /* Network information */
451 { "CSMSG_NETWORK_INFO", "Network Information:" },
452 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
453 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
454 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
455 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
456 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
457 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
458 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
459 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
462 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
463 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
464 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
466 /* Channel searches */
467 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
468 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
469 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
470 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
472 /* Channel configuration */
473 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
474 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
475 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
476 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
479 { "CSMSG_USER_OPTIONS", "User Options:" },
480 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
483 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
484 { "CSMSG_PING_RESPONSE", "Pong!" },
485 { "CSMSG_WUT_RESPONSE", "wut" },
486 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
487 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
488 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
489 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
490 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
491 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
492 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
495 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
496 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
497 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
498 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
499 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
500 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
501 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
502 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
503 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
504 { "CSMSG_VOTE_QUESTION", "Question: %s" },
505 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
506 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
507 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
508 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
509 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
510 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
511 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
512 { "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." },
513 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
514 { "CSMSG_VOTE_VOTED", "You have already voted." },
515 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
516 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
517 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
518 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
521 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
525 /* eject_user and unban_user flags */
526 #define ACTION_KICK 0x0001
527 #define ACTION_BAN 0x0002
528 #define ACTION_ADD_BAN 0x0004
529 #define ACTION_ADD_TIMED_BAN 0x0008
530 #define ACTION_UNBAN 0x0010
531 #define ACTION_DEL_BAN 0x0020
533 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
534 #define MODELEN 40 + KEYLEN
538 #define CSFUNC_ARGS user, channel, argc, argv, cmd
540 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
541 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
542 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
543 reply("MSG_MISSING_PARAMS", argv[0]); \
547 DECLARE_LIST(dnrList, struct do_not_register *);
548 DEFINE_LIST(dnrList, struct do_not_register *)
550 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
552 struct userNode *chanserv;
555 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
556 static struct log_type *CS_LOG;
560 struct channelList support_channels;
561 struct mod_chanmode default_modes;
563 unsigned long db_backup_frequency;
564 unsigned long channel_expire_frequency;
565 unsigned long dnr_expire_frequency;
567 unsigned long invited_timeout;
569 unsigned long info_delay;
570 unsigned long adjust_delay;
571 unsigned long channel_expire_delay;
572 unsigned int nodelete_level;
574 unsigned int adjust_threshold;
575 int join_flood_threshold;
577 unsigned int greeting_length;
578 unsigned int refresh_period;
579 unsigned int giveownership_period;
581 unsigned int max_owned;
582 unsigned int max_chan_users;
583 unsigned int max_chan_bans;
584 unsigned int max_userinfo_length;
586 unsigned int revoke_mode_a;
588 struct string_list *set_shows;
589 struct string_list *eightball;
590 struct string_list *old_ban_names;
592 const char *ctcp_short_ban_duration;
593 const char *ctcp_long_ban_duration;
595 const char *irc_operator_epithet;
596 const char *network_helper_epithet;
597 const char *support_helper_epithet;
599 const char *new_channel_authed;
600 const char *new_channel_unauthed;
601 const char *new_channel_msg;
606 struct userNode *user;
607 struct userNode *bot;
608 struct chanNode *channel;
610 unsigned short lowest;
611 unsigned short highest;
612 struct userData **users;
613 struct helpfile_table table;
618 struct userNode *user;
619 struct chanNode *chan;
622 enum note_access_type
624 NOTE_SET_CHANNEL_ACCESS,
625 NOTE_SET_CHANNEL_SETTER,
629 enum note_visible_type
632 NOTE_VIS_CHANNEL_USERS,
638 enum note_access_type set_access_type;
640 unsigned int min_opserv;
641 unsigned short min_ulevel;
643 enum note_visible_type visible_type;
644 unsigned int max_length;
651 struct note_type *type;
652 char setter[NICKSERV_HANDLE_LEN+1];
656 static unsigned int registered_channels;
657 static unsigned int banCount;
659 static const struct {
662 unsigned short level;
665 { "peon", "Peon", UL_PEON, '+' },
666 { "op", "Op", UL_OP, '@' },
667 { "master", "Master", UL_MASTER, '%' },
668 { "coowner", "Coowner", UL_COOWNER, '*' },
669 { "owner", "Owner", UL_OWNER, '!' },
670 { "helper", "BUG:", UL_HELPER, 'X' }
673 static const struct {
676 unsigned short default_value;
677 unsigned int old_idx;
678 unsigned int old_flag;
679 unsigned short flag_value;
681 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
682 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
683 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
684 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
685 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
686 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
687 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
688 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
689 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
690 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
691 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
692 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
695 struct charOptionValues {
698 } protectValues[] = {
699 { 'a', "CSMSG_PROTECT_ALL" },
700 { 'e', "CSMSG_PROTECT_EQUAL" },
701 { 'l', "CSMSG_PROTECT_LOWER" },
702 { 'n', "CSMSG_PROTECT_NONE" }
704 { 'd', "CSMSG_TOYS_DISABLED" },
705 { 'n', "CSMSG_TOYS_PRIVATE" },
706 { 'p', "CSMSG_TOYS_PUBLIC" }
707 }, topicRefreshValues[] = {
708 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
709 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
710 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
711 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
712 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
713 }, ctcpReactionValues[] = {
714 { 'k', "CSMSG_CTCPREACTION_KICK" },
715 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
716 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
717 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
720 static const struct {
724 unsigned int old_idx;
726 struct charOptionValues *values;
728 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
729 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
730 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
731 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
734 struct userData *helperList;
735 struct chanData *channelList;
736 static struct module *chanserv_module;
737 static unsigned int userCount;
739 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
740 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
741 static void unregister_channel(struct chanData *channel, const char *reason);
744 user_level_from_name(const char *name, unsigned short clamp_level)
746 unsigned int level = 0, ii;
748 level = strtoul(name, NULL, 10);
749 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
750 if(!irccasecmp(name, accessLevels[ii].name))
751 level = accessLevels[ii].level;
752 if(level > clamp_level)
758 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
761 *minl = strtoul(arg, &sep, 10);
769 *maxl = strtoul(sep+1, &sep, 10);
777 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
779 struct userData *uData, **head;
781 if(!channel || !handle)
784 if(override && HANDLE_FLAGGED(handle, HELPING)
785 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
787 for(uData = helperList;
788 uData && uData->handle != handle;
789 uData = uData->next);
793 uData = calloc(1, sizeof(struct userData));
794 uData->handle = handle;
796 uData->access = UL_HELPER;
802 uData->next = helperList;
804 helperList->prev = uData;
812 for(uData = channel->users; uData; uData = uData->next)
813 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
816 head = &(channel->users);
819 if(uData && (uData != *head))
821 /* Shuffle the user to the head of whatever list he was in. */
823 uData->next->prev = uData->prev;
825 uData->prev->next = uData->next;
831 (**head).prev = uData;
838 /* Returns non-zero if user has at least the minimum access.
839 * exempt_owner is set when handling !set, so the owner can set things
842 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
844 struct userData *uData;
845 struct chanData *cData = channel->channel_info;
846 unsigned short minimum = cData->lvlOpts[opt];
849 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
852 if(minimum <= uData->access)
854 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
859 /* Scan for other users authenticated to the same handle
860 still in the channel. If so, keep them listed as present.
862 user is optional, if not null, it skips checking that userNode
863 (for the handle_part function) */
865 scan_user_presence(struct userData *uData, struct userNode *user)
869 if(IsSuspended(uData->channel)
870 || IsUserSuspended(uData)
871 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
883 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
885 unsigned int eflags, argc;
887 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
889 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
890 if(!channel->channel_info
891 || IsSuspended(channel->channel_info)
893 || !ircncasecmp(text, "ACTION ", 7))
895 /* Figure out the minimum level needed to CTCP the channel */
896 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
898 /* We need to enforce against them; do so. */
900 argv[0] = (char*)text;
901 argv[1] = user->nick;
903 if(GetUserMode(channel, user))
904 eflags |= ACTION_KICK;
905 switch(channel->channel_info->chOpts[chCTCPReaction]) {
906 default: case 'k': /* just do the kick */ break;
908 eflags |= ACTION_BAN;
911 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
912 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
915 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
916 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
919 argv[argc++] = bad_ctcp_reason;
920 eject_user(chanserv, channel, argc, argv, NULL, eflags);
924 chanserv_create_note_type(const char *name)
926 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
927 strcpy(ntype->name, name);
929 dict_insert(note_types, ntype->name, ntype);
934 free_vote_options(void *data)
936 struct vote_option *vOpt = data;
938 free(vOpt->option_str);
943 chanserv_deref_note_type(void *data)
945 struct note_type *ntype = data;
947 if(--ntype->refs > 0)
953 chanserv_flush_note_type(struct note_type *ntype)
955 struct chanData *cData;
956 for(cData = channelList; cData; cData = cData->next)
957 dict_remove(cData->notes, ntype->name);
961 chanserv_truncate_notes(struct note_type *ntype)
963 struct chanData *cData;
965 unsigned int size = sizeof(*note) + ntype->max_length;
967 for(cData = channelList; cData; cData = cData->next) {
968 note = dict_find(cData->notes, ntype->name, NULL);
971 if(strlen(note->note) <= ntype->max_length)
973 dict_remove2(cData->notes, ntype->name, 1);
974 note = realloc(note, size);
975 note->note[ntype->max_length] = 0;
976 dict_insert(cData->notes, ntype->name, note);
980 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
983 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
986 unsigned int len = strlen(text);
988 if(len > type->max_length) len = type->max_length;
989 note = calloc(1, sizeof(*note) + len);
991 strncpy(note->setter, setter, sizeof(note->setter)-1);
992 memcpy(note->note, text, len);
994 dict_insert(channel->notes, type->name, note);
1000 chanserv_free_note(void *data)
1002 struct note *note = data;
1004 chanserv_deref_note_type(note->type);
1005 assert(note->type->refs > 0); /* must use delnote to remove the type */
1009 static MODCMD_FUNC(cmd_createnote) {
1010 struct note_type *ntype;
1011 unsigned int arg = 1, existed = 0, max_length;
1013 if((ntype = dict_find(note_types, argv[1], NULL)))
1016 ntype = chanserv_create_note_type(argv[arg]);
1017 if(!irccasecmp(argv[++arg], "privileged"))
1020 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1021 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1023 else if(!irccasecmp(argv[arg], "channel"))
1025 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1028 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1031 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1032 ntype->set_access.min_ulevel = ulvl;
1034 else if(!irccasecmp(argv[arg], "setter"))
1036 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1040 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1044 if(!irccasecmp(argv[++arg], "privileged"))
1045 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1046 else if(!irccasecmp(argv[arg], "channel_users"))
1047 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1048 else if(!irccasecmp(argv[arg], "all"))
1049 ntype->visible_type = NOTE_VIS_ALL;
1051 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1055 if((arg+1) >= argc) {
1056 reply("MSG_MISSING_PARAMS", argv[0]);
1059 max_length = strtoul(argv[++arg], NULL, 0);
1060 if(max_length < 20 || max_length > 450)
1062 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1065 if(existed && (max_length < ntype->max_length))
1067 ntype->max_length = max_length;
1068 chanserv_truncate_notes(ntype);
1070 ntype->max_length = max_length;
1073 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1075 reply("CSMSG_NOTE_CREATED", ntype->name);
1080 dict_remove(note_types, ntype->name);
1084 static MODCMD_FUNC(cmd_removenote) {
1085 struct note_type *ntype;
1088 ntype = dict_find(note_types, argv[1], NULL);
1089 force = (argc > 2) && !irccasecmp(argv[2], "force");
1092 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1099 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1102 chanserv_flush_note_type(ntype);
1104 dict_remove(note_types, argv[1]);
1105 reply("CSMSG_NOTE_DELETED", argv[1]);
1110 chanserv_expire_channel(void *data)
1112 struct chanData *channel = data;
1113 char reason[MAXLEN];
1114 sprintf(reason, "channel expired.");
1115 channel->expiry = 0;
1116 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1117 unregister_channel(channel, reason);
1120 static MODCMD_FUNC(chan_opt_expire)
1122 struct chanData *cData = channel->channel_info;
1123 unsigned long value = cData->expiry;
1127 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1129 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1132 unsigned long expiry,duration;
1134 /* The two directions can have different ACLs. */
1135 if(!strcmp(argv[1], "0"))
1137 else if((duration = ParseInterval(argv[1])))
1138 expiry = now + duration;
1141 reply("MSG_INVALID_DURATION", argv[1]);
1145 if (expiry != value)
1149 timeq_del(value, chanserv_expire_channel, cData, 0);
1152 cData->expiry = value;
1155 timeq_add(expiry, chanserv_expire_channel, cData);
1160 if(cData->expiry > now) {
1161 char expirestr[INTERVALLEN];
1162 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1164 reply("CSMSG_SET_EXPIRE_OFF");
1169 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1173 if(orig->modes_set & change->modes_clear)
1175 if(orig->modes_clear & change->modes_set)
1177 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1178 && strcmp(orig->new_key, change->new_key))
1180 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1181 && (orig->new_limit != change->new_limit))
1186 static char max_length_text[MAXLEN+1][16];
1188 static struct helpfile_expansion
1189 chanserv_expand_variable(const char *variable)
1191 struct helpfile_expansion exp;
1193 if(!irccasecmp(variable, "notes"))
1196 exp.type = HF_TABLE;
1197 exp.value.table.length = 1;
1198 exp.value.table.width = 3;
1199 exp.value.table.flags = 0;
1200 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1201 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1202 exp.value.table.contents[0][0] = "Note Type";
1203 exp.value.table.contents[0][1] = "Visibility";
1204 exp.value.table.contents[0][2] = "Max Length";
1205 for(it=dict_first(note_types); it; it=iter_next(it))
1207 struct note_type *ntype = iter_data(it);
1210 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1211 row = exp.value.table.length++;
1212 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1213 exp.value.table.contents[row][0] = ntype->name;
1214 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1215 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1217 if(!max_length_text[ntype->max_length][0])
1218 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1219 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1224 exp.type = HF_STRING;
1225 exp.value.str = NULL;
1229 static struct chanData*
1230 register_channel(struct chanNode *cNode, char *registrar)
1232 struct chanData *channel;
1233 enum levelOption lvlOpt;
1234 enum charOption chOpt;
1237 channel = calloc(1, sizeof(struct chanData));
1239 channel->notes = dict_new();
1240 dict_set_free_data(channel->notes, chanserv_free_note);
1242 channel->registrar = strdup(registrar);
1243 channel->registered = now;
1244 channel->visited = now;
1245 channel->limitAdjusted = now;
1246 channel->ownerTransfer = now;
1247 channel->flags = CHANNEL_DEFAULT_FLAGS;
1248 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1249 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1250 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1251 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1252 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1253 channel->advtopic[i] = NULL;
1255 channel->prev = NULL;
1256 channel->next = channelList;
1259 channelList->prev = channel;
1260 channelList = channel;
1261 registered_channels++;
1263 channel->channel = cNode;
1265 cNode->channel_info = channel;
1267 channel->vote = NULL;
1272 static struct userData*
1273 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1275 struct userData *ud;
1277 if(access_level > UL_OWNER)
1280 ud = calloc(1, sizeof(*ud));
1281 ud->channel = channel;
1282 ud->handle = handle;
1284 ud->access = access_level;
1285 ud->info = info ? strdup(info) : NULL;
1288 ud->next = channel->users;
1290 channel->users->prev = ud;
1291 channel->users = ud;
1293 channel->userCount++;
1297 ud->u_next = ud->handle->channels;
1299 ud->u_next->u_prev = ud;
1300 ud->handle->channels = ud;
1306 del_channel_user(struct userData *user, int do_gc)
1308 struct chanData *channel = user->channel;
1310 channel->userCount--;
1314 user->prev->next = user->next;
1316 channel->users = user->next;
1318 user->next->prev = user->prev;
1321 user->u_prev->u_next = user->u_next;
1323 user->handle->channels = user->u_next;
1325 user->u_next->u_prev = user->u_prev;
1329 if(do_gc && !channel->users && !IsProtected(channel)) {
1330 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1331 unregister_channel(channel, "lost all users.");
1335 static void expire_ban(void *data);
1338 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1341 unsigned int ii, l1, l2;
1346 bd = malloc(sizeof(struct banData));
1348 bd->channel = channel;
1350 bd->triggered = triggered;
1351 bd->expires = expires;
1353 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1355 extern const char *hidden_host_suffix;
1356 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1360 l2 = strlen(old_name);
1363 if(irccasecmp(mask + l1 - l2, old_name))
1365 new_mask = alloca(MAXLEN);
1366 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1369 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1371 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1372 bd->reason = strdup(reason);
1375 timeq_add(expires, expire_ban, bd);
1378 bd->next = channel->bans;
1380 channel->bans->prev = bd;
1382 channel->banCount++;
1389 del_channel_ban(struct banData *ban)
1391 ban->channel->banCount--;
1395 ban->prev->next = ban->next;
1397 ban->channel->bans = ban->next;
1400 ban->next->prev = ban->prev;
1403 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1412 expire_ban(void *data)
1414 struct banData *bd = data;
1415 if(!IsSuspended(bd->channel))
1417 struct banList bans;
1418 struct mod_chanmode change;
1420 bans = bd->channel->channel->banlist;
1421 mod_chanmode_init(&change);
1422 for(ii=0; ii<bans.used; ii++)
1424 if(!strcmp(bans.list[ii]->ban, bd->mask))
1427 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1428 change.args[0].u.hostmask = bd->mask;
1429 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1435 del_channel_ban(bd);
1438 static void chanserv_expire_suspension(void *data);
1441 unregister_channel(struct chanData *channel, const char *reason)
1443 struct mod_chanmode change;
1444 char msgbuf[MAXLEN];
1447 /* After channel unregistration, the following must be cleaned
1449 - Channel information.
1452 - Channel suspension data.
1453 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1459 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1461 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1462 mod_chanmode_init(&change);
1464 change.modes_clear |= MODE_REGISTERED;
1465 if(chanserv_conf.revoke_mode_a)
1466 change.modes_clear |= MODE_ACCESS;
1467 mod_chanmode_announce(chanserv, channel->channel, &change);
1470 while(channel->users)
1471 del_channel_user(channel->users, 0);
1473 while(channel->bans)
1474 del_channel_ban(channel->bans);
1476 free(channel->topic);
1477 free(channel->registrar);
1478 free(channel->greeting);
1479 free(channel->user_greeting);
1480 free(channel->topic_mask);
1482 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1483 if(channel->advtopic[i])
1484 free(channel->advtopic[i]);
1488 channel->prev->next = channel->next;
1490 channelList = channel->next;
1493 channel->next->prev = channel->prev;
1495 if(channel->suspended)
1497 struct chanNode *cNode = channel->channel;
1498 struct suspended *suspended, *next_suspended;
1500 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1502 next_suspended = suspended->previous;
1503 free(suspended->suspender);
1504 free(suspended->reason);
1505 if(suspended->expires)
1506 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1511 cNode->channel_info = NULL;
1514 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1515 channel->channel->channel_info = NULL;
1517 dict_delete(channel->notes);
1518 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1519 if(!IsSuspended(channel))
1520 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1521 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1522 UnlockChannel(channel->channel);
1524 registered_channels--;
1528 expire_channels(void *data)
1530 struct chanData *channel, *next;
1531 struct userData *user;
1532 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1534 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1535 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1537 for(channel = channelList; channel; channel = next)
1539 next = channel->next;
1541 /* See if the channel can be expired. */
1542 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1543 || IsProtected(channel))
1546 /* Make sure there are no high-ranking users still in the channel. */
1547 for(user=channel->users; user; user=user->next)
1548 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1553 /* Unregister the channel */
1554 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1555 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1556 unregister_channel(channel, "registration expired.");
1559 if(chanserv_conf.channel_expire_frequency && !data)
1560 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1564 expire_dnrs(UNUSED_ARG(void *data))
1566 dict_iterator_t it, next;
1567 struct do_not_register *dnr;
1569 for(it = dict_first(handle_dnrs); it; it = next)
1571 dnr = iter_data(it);
1572 next = iter_next(it);
1573 if(dnr->expires && dnr->expires <= now)
1574 dict_remove(handle_dnrs, dnr->chan_name + 1);
1576 for(it = dict_first(plain_dnrs); it; it = next)
1578 dnr = iter_data(it);
1579 next = iter_next(it);
1580 if(dnr->expires && dnr->expires <= now)
1581 dict_remove(plain_dnrs, dnr->chan_name + 1);
1583 for(it = dict_first(mask_dnrs); it; it = next)
1585 dnr = iter_data(it);
1586 next = iter_next(it);
1587 if(dnr->expires && dnr->expires <= now)
1588 dict_remove(mask_dnrs, dnr->chan_name + 1);
1591 if(chanserv_conf.dnr_expire_frequency)
1592 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1596 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1598 char protect = channel->chOpts[chProtect];
1599 struct userData *cs_victim, *cs_aggressor;
1601 /* Don't protect if no one is to be protected, someone is attacking
1602 himself, or if the aggressor is an IRC Operator. */
1603 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1606 /* Don't protect if the victim isn't authenticated (because they
1607 can't be a channel user), unless we are to protect non-users
1609 cs_victim = GetChannelAccess(channel, victim->handle_info);
1610 if(protect != 'a' && !cs_victim)
1613 /* Protect if the aggressor isn't a user because at this point,
1614 the aggressor can only be less than or equal to the victim. */
1615 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1619 /* If the aggressor was a user, then the victim can't be helped. */
1626 if(cs_victim->access > cs_aggressor->access)
1631 if(cs_victim->access >= cs_aggressor->access)
1640 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1642 struct chanData *cData = channel->channel_info;
1643 struct userData *cs_victim;
1645 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1646 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1647 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1649 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1657 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1659 if(IsService(victim))
1661 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1665 if(protect_user(victim, user, channel->channel_info))
1667 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1674 static struct do_not_register *
1675 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1677 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1678 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1679 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1680 strcpy(dnr->reason, reason);
1682 dnr->expires = expires;
1683 if(dnr->chan_name[0] == '*')
1684 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1685 else if(strpbrk(dnr->chan_name, "*?"))
1686 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1688 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1692 static struct dnrList
1693 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1695 struct dnrList list;
1696 dict_iterator_t it, next;
1697 struct do_not_register *dnr;
1699 dnrList_init(&list);
1701 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1703 if(dnr->expires && dnr->expires <= now)
1704 dict_remove(handle_dnrs, handle);
1705 else if(list.used < max)
1706 dnrList_append(&list, dnr);
1709 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1711 if(dnr->expires && dnr->expires <= now)
1712 dict_remove(plain_dnrs, chan_name);
1713 else if(list.used < max)
1714 dnrList_append(&list, dnr);
1719 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1721 next = iter_next(it);
1722 if(!match_ircglob(chan_name, iter_key(it)))
1724 dnr = iter_data(it);
1725 if(dnr->expires && dnr->expires <= now)
1726 dict_remove(mask_dnrs, iter_key(it));
1728 dnrList_append(&list, dnr);
1735 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1737 struct userNode *user;
1738 char buf1[INTERVALLEN];
1739 char buf2[INTERVALLEN];
1746 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1751 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1752 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1756 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1759 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1764 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1766 struct dnrList list;
1769 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1770 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1771 dnr_print_func(list.list[ii], user);
1773 reply("CSMSG_MORE_DNRS", list.used - ii);
1778 struct do_not_register *
1779 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1781 struct dnrList list;
1782 struct do_not_register *dnr;
1784 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1785 dnr = list.used ? list.list[0] : NULL;
1790 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1792 struct do_not_register *dnr;
1793 dict_iterator_t it, next;
1794 unsigned int matches = 0;
1796 for(it = dict_first(dict); it; it = next)
1798 dnr = iter_data(it);
1799 next = iter_next(it);
1800 if(dnr->expires && dnr->expires <= now)
1802 dict_remove(dict, iter_key(it));
1805 dnr_print_func(dnr, user);
1812 static CHANSERV_FUNC(cmd_noregister)
1816 unsigned long expiry, duration;
1817 unsigned int matches;
1821 reply("CSMSG_DNR_SEARCH_RESULTS");
1822 matches = send_dnrs(user, handle_dnrs);
1823 matches += send_dnrs(user, plain_dnrs);
1824 matches += send_dnrs(user, mask_dnrs);
1826 reply("MSG_MATCH_COUNT", matches);
1828 reply("MSG_NO_MATCHES");
1834 if(!IsChannelName(target) && (*target != '*'))
1836 reply("CSMSG_NOT_DNR", target);
1844 reply("MSG_INVALID_DURATION", argv[2]);
1848 if(!strcmp(argv[2], "0"))
1850 else if((duration = ParseInterval(argv[2])))
1851 expiry = now + duration;
1854 reply("MSG_INVALID_DURATION", argv[2]);
1858 reason = unsplit_string(argv + 3, argc - 3, NULL);
1859 if((*target == '*') && !get_handle_info(target + 1))
1861 reply("MSG_HANDLE_UNKNOWN", target + 1);
1864 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1865 reply("CSMSG_NOREGISTER_CHANNEL", target);
1869 reply("CSMSG_DNR_SEARCH_RESULTS");
1871 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1873 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1875 reply("MSG_NO_MATCHES");
1879 static CHANSERV_FUNC(cmd_allowregister)
1881 const char *chan_name = argv[1];
1883 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1884 || dict_remove(plain_dnrs, chan_name)
1885 || dict_remove(mask_dnrs, chan_name))
1887 reply("CSMSG_DNR_REMOVED", chan_name);
1890 reply("CSMSG_NO_SUCH_DNR", chan_name);
1895 struct userNode *source;
1899 unsigned long min_set, max_set;
1900 unsigned long min_expires, max_expires;
1905 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1907 return !((dnr->set < search->min_set)
1908 || (dnr->set > search->max_set)
1909 || (dnr->expires < search->min_expires)
1910 || (search->max_expires
1911 && ((dnr->expires == 0)
1912 || (dnr->expires > search->max_expires)))
1913 || (search->chan_mask
1914 && !match_ircglob(dnr->chan_name, search->chan_mask))
1915 || (search->setter_mask
1916 && !match_ircglob(dnr->setter, search->setter_mask))
1917 || (search->reason_mask
1918 && !match_ircglob(dnr->reason, search->reason_mask)));
1921 static struct dnr_search *
1922 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1924 struct dnr_search *discrim;
1927 discrim = calloc(1, sizeof(*discrim));
1928 discrim->source = user;
1929 discrim->chan_mask = NULL;
1930 discrim->setter_mask = NULL;
1931 discrim->reason_mask = NULL;
1932 discrim->max_set = INT_MAX;
1933 discrim->limit = 50;
1935 for(ii=0; ii<argc; ++ii)
1939 reply("MSG_MISSING_PARAMS", argv[ii]);
1942 else if(0 == irccasecmp(argv[ii], "channel"))
1944 discrim->chan_mask = argv[++ii];
1946 else if(0 == irccasecmp(argv[ii], "setter"))
1948 discrim->setter_mask = argv[++ii];
1950 else if(0 == irccasecmp(argv[ii], "reason"))
1952 discrim->reason_mask = argv[++ii];
1954 else if(0 == irccasecmp(argv[ii], "limit"))
1956 discrim->limit = strtoul(argv[++ii], NULL, 0);
1958 else if(0 == irccasecmp(argv[ii], "set"))
1960 const char *cmp = argv[++ii];
1963 discrim->min_set = now - ParseInterval(cmp + 2);
1965 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1966 } else if(cmp[0] == '=') {
1967 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1968 } else if(cmp[0] == '>') {
1970 discrim->max_set = now - ParseInterval(cmp + 2);
1972 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1974 discrim->max_set = now - (ParseInterval(cmp) - 1);
1977 else if(0 == irccasecmp(argv[ii], "expires"))
1979 const char *cmp = argv[++ii];
1982 discrim->max_expires = now + ParseInterval(cmp + 2);
1984 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1985 } else if(cmp[0] == '=') {
1986 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1987 } else if(cmp[0] == '>') {
1989 discrim->min_expires = now + ParseInterval(cmp + 2);
1991 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1993 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1998 reply("MSG_INVALID_CRITERIA", argv[ii]);
2009 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2012 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2014 struct do_not_register *dnr;
2015 dict_iterator_t next;
2020 /* Initialize local variables. */
2023 if(discrim->chan_mask)
2025 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2026 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2030 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2032 /* Check against account-based DNRs. */
2033 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2034 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2037 else if(target_fixed)
2039 /* Check against channel-based DNRs. */
2040 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2041 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2046 /* Exhaustively search account DNRs. */
2047 for(it = dict_first(handle_dnrs); it; it = next)
2049 next = iter_next(it);
2050 dnr = iter_data(it);
2051 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2055 /* Do the same for channel DNRs. */
2056 for(it = dict_first(plain_dnrs); it; it = next)
2058 next = iter_next(it);
2059 dnr = iter_data(it);
2060 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2064 /* Do the same for wildcarded channel DNRs. */
2065 for(it = dict_first(mask_dnrs); it; it = next)
2067 next = iter_next(it);
2068 dnr = iter_data(it);
2069 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2077 dnr_remove_func(struct do_not_register *match, void *extra)
2079 struct userNode *user;
2082 chan_name = alloca(strlen(match->chan_name) + 1);
2083 strcpy(chan_name, match->chan_name);
2085 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2086 || dict_remove(plain_dnrs, chan_name)
2087 || dict_remove(mask_dnrs, chan_name))
2089 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2095 dnr_count_func(struct do_not_register *match, void *extra)
2097 return 0; (void)match; (void)extra;
2100 static MODCMD_FUNC(cmd_dnrsearch)
2102 struct dnr_search *discrim;
2103 dnr_search_func action;
2104 struct svccmd *subcmd;
2105 unsigned int matches;
2108 sprintf(buf, "dnrsearch %s", argv[1]);
2109 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2112 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2115 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2117 if(!irccasecmp(argv[1], "print"))
2118 action = dnr_print_func;
2119 else if(!irccasecmp(argv[1], "remove"))
2120 action = dnr_remove_func;
2121 else if(!irccasecmp(argv[1], "count"))
2122 action = dnr_count_func;
2125 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2129 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2133 if(action == dnr_print_func)
2134 reply("CSMSG_DNR_SEARCH_RESULTS");
2135 matches = dnr_search(discrim, action, user);
2137 reply("MSG_MATCH_COUNT", matches);
2139 reply("MSG_NO_MATCHES");
2145 chanserv_get_owned_count(struct handle_info *hi)
2147 struct userData *cList;
2150 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2151 if(cList->access == UL_OWNER)
2156 static CHANSERV_FUNC(cmd_register)
2158 struct handle_info *handle;
2159 struct chanData *cData;
2160 struct modeNode *mn;
2161 char reason[MAXLEN];
2163 unsigned int new_channel, force=0;
2164 struct do_not_register *dnr;
2168 if(channel->channel_info)
2170 reply("CSMSG_ALREADY_REGGED", channel->name);
2174 if(channel->bad_channel)
2176 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2181 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2183 reply("CSMSG_MUST_BE_OPPED", channel->name);
2188 chan_name = channel->name;
2192 if((argc < 2) || !IsChannelName(argv[1]))
2194 reply("MSG_NOT_CHANNEL_NAME");
2198 if(opserv_bad_channel(argv[1]))
2200 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2205 chan_name = argv[1];
2208 if(argc >= (new_channel+2))
2210 if(!IsHelping(user))
2212 reply("CSMSG_PROXY_FORBIDDEN");
2216 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2218 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2219 dnr = chanserv_is_dnr(chan_name, handle);
2223 handle = user->handle_info;
2224 dnr = chanserv_is_dnr(chan_name, handle);
2228 if(!IsHelping(user))
2229 reply("CSMSG_DNR_CHANNEL", chan_name);
2231 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2235 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2237 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2242 channel = AddChannel(argv[1], now, NULL, NULL);
2244 cData = register_channel(channel, user->handle_info->handle);
2245 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2246 cData->modes = chanserv_conf.default_modes;
2248 cData->modes.modes_set |= MODE_REGISTERED;
2249 if (IsOffChannel(cData))
2251 mod_chanmode_announce(chanserv, channel, &cData->modes);
2255 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2256 change->args[change->argc].mode = MODE_CHANOP;
2257 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2259 mod_chanmode_announce(chanserv, channel, change);
2260 mod_chanmode_free(change);
2263 /* Initialize the channel's max user record. */
2264 cData->max = channel->members.used;
2265 cData->max_time = 0;
2267 if(handle != user->handle_info)
2268 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2270 reply("CSMSG_REG_SUCCESS", channel->name);
2272 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2273 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2278 make_confirmation_string(struct userData *uData)
2280 static char strbuf[16];
2285 for(src = uData->handle->handle; *src; )
2286 accum = accum * 31 + toupper(*src++);
2288 for(src = uData->channel->channel->name; *src; )
2289 accum = accum * 31 + toupper(*src++);
2290 sprintf(strbuf, "%08x", accum);
2294 static CHANSERV_FUNC(cmd_unregister)
2297 char reason[MAXLEN];
2298 struct chanData *cData;
2299 struct userData *uData;
2301 cData = channel->channel_info;
2304 reply("CSMSG_NOT_REGISTERED", channel->name);
2308 uData = GetChannelUser(cData, user->handle_info);
2309 if(!uData || (uData->access < UL_OWNER))
2311 reply("CSMSG_NO_ACCESS");
2315 if(IsProtected(cData) && !IsOper(user))
2317 reply("CSMSG_UNREG_NODELETE", channel->name);
2321 if(!IsHelping(user))
2323 const char *confirm_string;
2324 if(IsSuspended(cData))
2326 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2329 confirm_string = make_confirmation_string(uData);
2330 if((argc < 2) || strcmp(argv[1], confirm_string))
2332 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2337 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2338 name = strdup(channel->name);
2339 unregister_channel(cData, reason);
2340 spamserv_cs_unregister(user, channel, manually, "unregistered");
2341 reply("CSMSG_UNREG_SUCCESS", name);
2347 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2349 extern struct userNode *spamserv;
2350 struct mod_chanmode *change;
2352 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2354 change = mod_chanmode_alloc(2);
2356 change->args[0].mode = MODE_CHANOP;
2357 change->args[0].u.member = AddChannelUser(chanserv, channel);
2358 change->args[1].mode = MODE_CHANOP;
2359 change->args[1].u.member = AddChannelUser(spamserv, channel);
2363 change = mod_chanmode_alloc(1);
2365 change->args[0].mode = MODE_CHANOP;
2366 change->args[0].u.member = AddChannelUser(chanserv, channel);
2369 mod_chanmode_announce(chanserv, channel, change);
2370 mod_chanmode_free(change);
2373 static CHANSERV_FUNC(cmd_move)
2375 struct mod_chanmode change;
2376 struct chanNode *target;
2377 struct modeNode *mn;
2378 struct userData *uData;
2379 char reason[MAXLEN];
2380 struct do_not_register *dnr;
2381 int chanserv_join = 0, spamserv_join;
2385 if(IsProtected(channel->channel_info))
2387 reply("CSMSG_MOVE_NODELETE", channel->name);
2391 if(!IsChannelName(argv[1]))
2393 reply("MSG_NOT_CHANNEL_NAME");
2397 if(opserv_bad_channel(argv[1]))
2399 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2403 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2405 for(uData = channel->channel_info->users; uData; uData = uData->next)
2407 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2409 if(!IsHelping(user))
2410 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2412 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2418 mod_chanmode_init(&change);
2419 if(!(target = GetChannel(argv[1])))
2421 target = AddChannel(argv[1], now, NULL, NULL);
2422 if(!IsSuspended(channel->channel_info))
2425 else if(target->channel_info)
2427 reply("CSMSG_ALREADY_REGGED", target->name);
2430 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2431 && !IsHelping(user))
2433 reply("CSMSG_MUST_BE_OPPED", target->name);
2436 else if(!IsSuspended(channel->channel_info))
2441 /* Clear MODE_REGISTERED from old channel, add it to new. */
2443 change.modes_clear = MODE_REGISTERED;
2444 mod_chanmode_announce(chanserv, channel, &change);
2445 change.modes_clear = 0;
2446 change.modes_set = MODE_REGISTERED;
2447 mod_chanmode_announce(chanserv, target, &change);
2450 /* Move the channel_info to the target channel; it
2451 shouldn't be necessary to clear timeq callbacks
2452 for the old channel. */
2453 target->channel_info = channel->channel_info;
2454 target->channel_info->channel = target;
2455 channel->channel_info = NULL;
2457 /* Check whether users are present in the new channel. */
2458 for(uData = target->channel_info->users; uData; uData = uData->next)
2459 scan_user_presence(uData, NULL);
2461 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2464 ss_cs_join_channel(target, spamserv_join);
2466 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2467 if(!IsSuspended(target->channel_info))
2469 char reason2[MAXLEN];
2470 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2471 DelChannelUser(chanserv, channel, reason2, 0);
2473 UnlockChannel(channel);
2474 LockChannel(target);
2475 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2476 reply("CSMSG_MOVE_SUCCESS", target->name);
2481 merge_users(struct chanData *source, struct chanData *target)
2483 struct userData *suData, *tuData, *next;
2489 /* Insert the source's users into the scratch area. */
2490 for(suData = source->users; suData; suData = suData->next)
2491 dict_insert(merge, suData->handle->handle, suData);
2493 /* Iterate through the target's users, looking for
2494 users common to both channels. The lower access is
2495 removed from either the scratch area or target user
2497 for(tuData = target->users; tuData; tuData = next)
2499 struct userData *choice;
2501 next = tuData->next;
2503 /* If a source user exists with the same handle as a target
2504 channel's user, resolve the conflict by removing one. */
2505 suData = dict_find(merge, tuData->handle->handle, NULL);
2509 /* Pick the data we want to keep. */
2510 /* If the access is the same, use the later seen time. */
2511 if(suData->access == tuData->access)
2512 choice = (suData->seen > tuData->seen) ? suData : tuData;
2513 else /* Otherwise, keep the higher access level. */
2514 choice = (suData->access > tuData->access) ? suData : tuData;
2515 /* Use the later seen time. */
2516 if(suData->seen < tuData->seen)
2517 suData->seen = tuData->seen;
2519 tuData->seen = suData->seen;
2521 /* Remove the user that wasn't picked. */
2522 if(choice == tuData)
2524 dict_remove(merge, suData->handle->handle);
2525 del_channel_user(suData, 0);
2528 del_channel_user(tuData, 0);
2531 /* Move the remaining users to the target channel. */
2532 for(it = dict_first(merge); it; it = iter_next(it))
2534 suData = iter_data(it);
2536 /* Insert the user into the target channel's linked list. */
2537 suData->prev = NULL;
2538 suData->next = target->users;
2539 suData->channel = target;
2542 target->users->prev = suData;
2543 target->users = suData;
2545 /* Update the user counts for the target channel; the
2546 source counts are left alone. */
2547 target->userCount++;
2549 /* Check whether the user is in the target channel. */
2550 scan_user_presence(suData, NULL);
2553 /* Possible to assert (source->users == NULL) here. */
2554 source->users = NULL;
2559 merge_bans(struct chanData *source, struct chanData *target)
2561 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2563 /* Hold on to the original head of the target ban list
2564 to avoid comparing source bans with source bans. */
2565 tFront = target->bans;
2567 /* Perform a totally expensive O(n*m) merge, ick. */
2568 for(sbData = source->bans; sbData; sbData = sNext)
2570 /* Flag to track whether the ban's been moved
2571 to the destination yet. */
2574 /* Possible to assert (sbData->prev == NULL) here. */
2575 sNext = sbData->next;
2577 for(tbData = tFront; tbData; tbData = tNext)
2579 tNext = tbData->next;
2581 /* Perform two comparisons between each source
2582 and target ban, conflicts are resolved by
2583 keeping the broader ban and copying the later
2584 expiration and triggered time. */
2585 if(match_ircglobs(tbData->mask, sbData->mask))
2587 /* There is a broader ban in the target channel that
2588 overrides one in the source channel; remove the
2589 source ban and break. */
2590 if(sbData->expires > tbData->expires)
2591 tbData->expires = sbData->expires;
2592 if(sbData->triggered > tbData->triggered)
2593 tbData->triggered = sbData->triggered;
2594 del_channel_ban(sbData);
2597 else if(match_ircglobs(sbData->mask, tbData->mask))
2599 /* There is a broader ban in the source channel that
2600 overrides one in the target channel; remove the
2601 target ban, fall through and move the source over. */
2602 if(tbData->expires > sbData->expires)
2603 sbData->expires = tbData->expires;
2604 if(tbData->triggered > sbData->triggered)
2605 sbData->triggered = tbData->triggered;
2606 if(tbData == tFront)
2608 del_channel_ban(tbData);
2611 /* Source bans can override multiple target bans, so
2612 we allow a source to run through this loop multiple
2613 times, but we can only move it once. */
2618 /* Remove the source ban from the source ban list. */
2620 sbData->next->prev = sbData->prev;
2622 /* Modify the source ban's associated channel. */
2623 sbData->channel = target;
2625 /* Insert the ban into the target channel's linked list. */
2626 sbData->prev = NULL;
2627 sbData->next = target->bans;
2630 target->bans->prev = sbData;
2631 target->bans = sbData;
2633 /* Update the user counts for the target channel. */
2638 /* Possible to assert (source->bans == NULL) here. */
2639 source->bans = NULL;
2643 merge_data(struct chanData *source, struct chanData *target)
2645 /* Use more recent visited and owner-transfer time; use older
2646 * registered time. Bitwise or may_opchan. Use higher max.
2647 * Do not touch last_refresh, ban count or user counts.
2649 if(source->visited > target->visited)
2650 target->visited = source->visited;
2651 if(source->registered < target->registered)
2652 target->registered = source->registered;
2653 if(source->ownerTransfer > target->ownerTransfer)
2654 target->ownerTransfer = source->ownerTransfer;
2655 if(source->may_opchan)
2656 target->may_opchan = 1;
2657 if(source->max > target->max) {
2658 target->max = source->max;
2659 target->max_time = source->max_time;
2664 merge_channel(struct chanData *source, struct chanData *target)
2666 merge_users(source, target);
2667 merge_bans(source, target);
2668 merge_data(source, target);
2671 static CHANSERV_FUNC(cmd_merge)
2673 struct userData *target_user;
2674 struct chanNode *target;
2675 char reason[MAXLEN];
2679 /* Make sure the target channel exists and is registered to the user
2680 performing the command. */
2681 if(!(target = GetChannel(argv[1])))
2683 reply("MSG_INVALID_CHANNEL");
2687 if(!target->channel_info)
2689 reply("CSMSG_NOT_REGISTERED", target->name);
2693 if(IsProtected(channel->channel_info))
2695 reply("CSMSG_MERGE_NODELETE");
2699 if(IsSuspended(target->channel_info))
2701 reply("CSMSG_MERGE_SUSPENDED");
2705 if(channel == target)
2707 reply("CSMSG_MERGE_SELF");
2711 target_user = GetChannelUser(target->channel_info, user->handle_info);
2712 if(!target_user || (target_user->access < UL_OWNER))
2714 reply("CSMSG_MERGE_NOT_OWNER");
2718 /* Merge the channel structures and associated data. */
2719 merge_channel(channel->channel_info, target->channel_info);
2720 spamserv_cs_move_merge(user, channel, target, 0);
2721 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2722 unregister_channel(channel->channel_info, reason);
2723 reply("CSMSG_MERGE_SUCCESS", target->name);
2727 static CHANSERV_FUNC(cmd_opchan)
2729 struct mod_chanmode change;
2730 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2732 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2735 channel->channel_info->may_opchan = 0;
2736 mod_chanmode_init(&change);
2738 change.args[0].mode = MODE_CHANOP;
2739 change.args[0].u.member = GetUserMode(channel, chanserv);
2740 if(!change.args[0].u.member)
2742 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2745 mod_chanmode_announce(chanserv, channel, &change);
2746 reply("CSMSG_OPCHAN_DONE", channel->name);
2750 static CHANSERV_FUNC(cmd_adduser)
2752 struct userData *actee;
2753 struct userData *actor, *real_actor;
2754 struct handle_info *handle;
2755 unsigned short access_level, override = 0;
2759 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2761 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2765 access_level = user_level_from_name(argv[2], UL_OWNER);
2768 reply("CSMSG_INVALID_ACCESS", argv[2]);
2772 actor = GetChannelUser(channel->channel_info, user->handle_info);
2773 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2775 if(actor->access <= access_level)
2777 reply("CSMSG_NO_BUMP_ACCESS");
2781 /* Trying to add someone with equal/more access? */
2782 if (!real_actor || real_actor->access <= access_level)
2783 override = CMD_LOG_OVERRIDE;
2785 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2788 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2790 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2794 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2795 scan_user_presence(actee, NULL);
2796 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2797 return 1 | override;
2800 static CHANSERV_FUNC(cmd_clvl)
2802 struct handle_info *handle;
2803 struct userData *victim;
2804 struct userData *actor, *real_actor;
2805 unsigned short new_access, override = 0;
2806 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2810 actor = GetChannelUser(channel->channel_info, user->handle_info);
2811 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2813 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2816 if(handle == user->handle_info && !privileged)
2818 reply("CSMSG_NO_SELF_CLVL");
2822 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2824 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2828 if(actor->access <= victim->access && !privileged)
2830 reply("MSG_USER_OUTRANKED", handle->handle);
2834 new_access = user_level_from_name(argv[2], UL_OWNER);
2838 reply("CSMSG_INVALID_ACCESS", argv[2]);
2842 if(new_access >= actor->access && !privileged)
2844 reply("CSMSG_NO_BUMP_ACCESS");
2848 /* Trying to clvl a equal/higher user? */
2849 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2850 override = CMD_LOG_OVERRIDE;
2851 /* Trying to clvl someone to equal/higher access? */
2852 if(!real_actor || new_access >= real_actor->access)
2853 override = CMD_LOG_OVERRIDE;
2854 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2855 * If they lower their own access it's not a big problem.
2858 victim->access = new_access;
2859 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2860 return 1 | override;
2863 static CHANSERV_FUNC(cmd_deluser)
2865 struct handle_info *handle;
2866 struct userData *victim;
2867 struct userData *actor, *real_actor;
2868 unsigned short access_level, override = 0;
2873 actor = GetChannelUser(channel->channel_info, user->handle_info);
2874 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2876 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2879 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2881 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2887 access_level = user_level_from_name(argv[1], UL_OWNER);
2890 reply("CSMSG_INVALID_ACCESS", argv[1]);
2893 if(access_level != victim->access)
2895 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2901 access_level = victim->access;
2904 if((actor->access <= victim->access) && !IsHelping(user))
2906 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2910 /* If people delete themselves it is an override, but they
2911 * could've used deleteme so we don't log it as an override
2913 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2914 override = CMD_LOG_OVERRIDE;
2916 chan_name = strdup(channel->name);
2917 del_channel_user(victim, 1);
2918 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2920 return 1 | override;
2924 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2926 struct userData *actor, *real_actor, *uData, *next;
2927 unsigned int override = 0;
2929 actor = GetChannelUser(channel->channel_info, user->handle_info);
2930 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2932 if(min_access > max_access)
2934 reply("CSMSG_BAD_RANGE", min_access, max_access);
2938 if(actor->access <= max_access)
2940 reply("CSMSG_NO_ACCESS");
2944 if(!real_actor || real_actor->access <= max_access)
2945 override = CMD_LOG_OVERRIDE;
2947 for(uData = channel->channel_info->users; uData; uData = next)
2951 if((uData->access >= min_access)
2952 && (uData->access <= max_access)
2953 && match_ircglob(uData->handle->handle, mask))
2954 del_channel_user(uData, 1);
2957 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2958 return 1 | override;
2961 static CHANSERV_FUNC(cmd_mdelowner)
2963 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2966 static CHANSERV_FUNC(cmd_mdelcoowner)
2968 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2971 static CHANSERV_FUNC(cmd_mdelmaster)
2973 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2976 static CHANSERV_FUNC(cmd_mdelop)
2978 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2981 static CHANSERV_FUNC(cmd_mdelpeon)
2983 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2987 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2989 struct banData *bData, *next;
2990 char interval[INTERVALLEN];
2992 unsigned long limit;
2995 limit = now - duration;
2996 for(bData = channel->channel_info->bans; bData; bData = next)
3000 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3003 del_channel_ban(bData);
3007 intervalString(interval, duration, user->handle_info);
3008 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3013 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3015 struct userData *actor, *uData, *next;
3016 char interval[INTERVALLEN];
3018 unsigned long limit;
3020 actor = GetChannelUser(channel->channel_info, user->handle_info);
3021 if(min_access > max_access)
3023 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3027 if(!actor || actor->access <= max_access)
3029 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3034 limit = now - duration;
3035 for(uData = channel->channel_info->users; uData; uData = next)
3039 if((uData->seen > limit)
3041 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3044 if(((uData->access >= min_access) && (uData->access <= max_access))
3045 || (!max_access && (uData->access < actor->access)))
3047 del_channel_user(uData, 1);
3055 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3057 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3061 static CHANSERV_FUNC(cmd_trim)
3063 unsigned long duration;
3064 unsigned short min_level, max_level;
3069 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3070 duration = ParseInterval(argv[2]);
3073 reply("CSMSG_CANNOT_TRIM");
3077 if(!irccasecmp(argv[1], "bans"))
3079 cmd_trim_bans(user, channel, duration);
3082 else if(!irccasecmp(argv[1], "users"))
3084 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3087 else if(parse_level_range(&min_level, &max_level, argv[1]))
3089 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3092 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3094 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3099 reply("CSMSG_INVALID_TRIM", argv[1]);
3104 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3105 to the user. cmd_all takes advantage of this. */
3106 static CHANSERV_FUNC(cmd_up)
3108 struct mod_chanmode change;
3109 struct userData *uData;
3112 mod_chanmode_init(&change);
3114 change.args[0].u.member = GetUserMode(channel, user);
3115 if(!change.args[0].u.member)
3118 reply("MSG_CHANNEL_ABSENT", channel->name);
3122 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3126 reply("CSMSG_GODMODE_UP", argv[0]);
3129 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3131 change.args[0].mode = MODE_CHANOP;
3132 errmsg = "CSMSG_ALREADY_OPPED";
3134 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3136 change.args[0].mode = MODE_VOICE;
3137 errmsg = "CSMSG_ALREADY_VOICED";
3142 reply("CSMSG_NO_ACCESS");
3145 change.args[0].mode &= ~change.args[0].u.member->modes;
3146 if(!change.args[0].mode)
3149 reply(errmsg, channel->name);
3152 modcmd_chanmode_announce(&change);
3156 static CHANSERV_FUNC(cmd_down)
3158 struct mod_chanmode change;
3160 mod_chanmode_init(&change);
3162 change.args[0].u.member = GetUserMode(channel, user);
3163 if(!change.args[0].u.member)
3166 reply("MSG_CHANNEL_ABSENT", channel->name);
3170 if(!change.args[0].u.member->modes)
3173 reply("CSMSG_ALREADY_DOWN", channel->name);
3177 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3178 modcmd_chanmode_announce(&change);
3182 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)
3184 struct userData *cList;
3186 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3188 if(IsSuspended(cList->channel)
3189 || IsUserSuspended(cList)
3190 || !GetUserMode(cList->channel->channel, user))
3193 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3199 static CHANSERV_FUNC(cmd_upall)
3201 return cmd_all(CSFUNC_ARGS, cmd_up);
3204 static CHANSERV_FUNC(cmd_downall)
3206 return cmd_all(CSFUNC_ARGS, cmd_down);
3209 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3210 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3213 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)
3215 unsigned int ii, valid;
3216 struct userNode *victim;
3217 struct mod_chanmode *change;
3219 change = mod_chanmode_alloc(argc - 1);
3221 for(ii=valid=0; ++ii < argc; )
3223 if(!(victim = GetUserH(argv[ii])))
3225 change->args[valid].mode = mode;
3226 change->args[valid].u.member = GetUserMode(channel, victim);
3227 if(!change->args[valid].u.member)
3229 if(validate && !validate(user, channel, victim))
3234 change->argc = valid;
3235 if(valid < (argc-1))
3236 reply("CSMSG_PROCESS_FAILED");
3239 modcmd_chanmode_announce(change);
3240 reply(action, channel->name);
3242 mod_chanmode_free(change);
3246 static CHANSERV_FUNC(cmd_op)
3248 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3251 static CHANSERV_FUNC(cmd_deop)
3253 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3256 static CHANSERV_FUNC(cmd_voice)
3258 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3261 static CHANSERV_FUNC(cmd_devoice)
3263 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3266 static CHANSERV_FUNC(cmd_opme)
3268 struct mod_chanmode change;
3271 mod_chanmode_init(&change);
3273 change.args[0].u.member = GetUserMode(channel, user);
3274 if(!change.args[0].u.member)
3277 reply("MSG_CHANNEL_ABSENT", channel->name);
3281 struct devnull_class *devnull;
3282 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3284 change.args[0].mode = MODE_CHANOP;
3285 errmsg = "CSMSG_ALREADY_OPPED";
3290 reply("CSMSG_NO_ACCESS");
3293 change.args[0].mode &= ~change.args[0].u.member->modes;
3294 if(!change.args[0].mode)
3297 reply(errmsg, channel->name);
3300 modcmd_chanmode_announce(&change);
3305 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3311 for(ii=0; ii<channel->members.used; ii++)
3313 struct modeNode *mn = channel->members.list[ii];
3315 if(IsService(mn->user))
3318 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3321 if(protect_user(mn->user, user, channel->channel_info))
3325 victims[(*victimCount)++] = mn;
3331 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3333 struct userNode *victim;
3334 struct modeNode **victims;
3335 unsigned int offset, n, victimCount, duration = 0;
3336 char *reason = "Bye.", *ban, *name;
3337 char interval[INTERVALLEN];
3339 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3340 REQUIRE_PARAMS(offset);
3341 if(argc > offset && IsNetServ(user))
3343 if(*argv[offset] == '$') {
3344 struct userNode *hib;
3345 const char *accountnameb = argv[offset] + 1;
3346 if(!(hib = GetUserH(accountnameb)))
3348 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3357 reason = unsplit_string(argv + offset, argc - offset, NULL);
3358 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3360 /* Truncate the reason to a length of TOPICLEN, as
3361 the ircd does; however, leave room for an ellipsis
3362 and the kicker's nick. */
3363 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3367 if((victim = GetUserH(argv[1])))
3369 victims = alloca(sizeof(victims[0]));
3370 victims[0] = GetUserMode(channel, victim);
3371 /* XXX: The comparison with ACTION_KICK is just because all
3372 * other actions can work on users outside the channel, and we
3373 * want to allow those (e.g. unbans) in that case. If we add
3374 * some other ejection action for in-channel users, change
3376 victimCount = victims[0] ? 1 : 0;
3378 if(IsService(victim))
3380 reply("MSG_SERVICE_IMMUNE", victim->nick);
3384 if((action == ACTION_KICK) && !victimCount)
3386 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3390 if(protect_user(victim, user, channel->channel_info))
3392 reply("CSMSG_USER_PROTECTED", victim->nick);
3396 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3397 name = victim->nick;
3399 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3401 struct handle_info *hi;
3402 extern const char *titlehost_suffix;
3403 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3404 const char *accountname = argv[1] + 1;
3406 if(!(hi = get_handle_info(accountname)))
3408 reply("MSG_HANDLE_UNKNOWN", accountname);
3412 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3413 victims = alloca(sizeof(victims[0]) * channel->members.used);
3415 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3417 reply("CSMSG_MASK_PROTECTED", banmask);
3421 if((action == ACTION_KICK) && (victimCount == 0))
3423 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3427 name = ban = strdup(banmask);
3431 if(!is_ircmask(argv[1]))
3433 reply("MSG_NICK_UNKNOWN", argv[1]);
3437 victims = alloca(sizeof(victims[0]) * channel->members.used);
3439 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3441 reply("CSMSG_MASK_PROTECTED", argv[1]);
3445 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3447 reply("CSMSG_LAME_MASK", argv[1]);
3451 if((action == ACTION_KICK) && (victimCount == 0))
3453 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3457 name = ban = strdup(argv[1]);
3460 /* Truncate the ban in place if necessary; we must ensure
3461 that 'ban' is a valid ban mask before sanitizing it. */
3462 sanitize_ircmask(ban);
3464 if(action & ACTION_ADD_BAN)
3466 struct banData *bData, *next;
3468 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3470 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3475 if(action & ACTION_ADD_TIMED_BAN)
3477 duration = ParseInterval(argv[2]);
3481 reply("CSMSG_DURATION_TOO_LOW");
3485 else if(duration > (86400 * 365 * 2))
3487 reply("CSMSG_DURATION_TOO_HIGH");
3493 for(bData = channel->channel_info->bans; bData; bData = next)
3495 if(match_ircglobs(bData->mask, ban))
3497 int exact = !irccasecmp(bData->mask, ban);
3499 /* The ban is redundant; there is already a ban
3500 with the same effect in place. */
3504 free(bData->reason);
3505 bData->reason = strdup(reason);
3506 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3508 reply("CSMSG_REASON_CHANGE", ban);
3512 if(exact && bData->expires)
3516 /* If the ban matches an existing one exactly,
3517 extend the expiration time if the provided
3518 duration is longer. */
3519 if(duration && (now + duration > bData->expires))
3521 bData->expires = now + duration;
3532 /* Delete the expiration timeq entry and
3533 requeue if necessary. */
3534 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3537 timeq_add(bData->expires, expire_ban, bData);
3541 /* automated kickban */
3544 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3546 reply("CSMSG_BAN_ADDED", name, channel->name);
3552 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3559 if(match_ircglobs(ban, bData->mask))
3561 /* The ban we are adding makes previously existing
3562 bans redundant; silently remove them. */
3563 del_channel_ban(bData);
3567 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);
3569 name = ban = strdup(bData->mask);
3573 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3575 extern const char *hidden_host_suffix;
3576 const char *old_name = chanserv_conf.old_ban_names->list[n];
3578 unsigned int l1, l2;
3581 l2 = strlen(old_name);
3584 if(irccasecmp(ban + l1 - l2, old_name))
3586 new_mask = malloc(MAXLEN);
3587 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3589 name = ban = new_mask;
3594 if(action & ACTION_BAN)
3596 unsigned int exists;
3597 struct mod_chanmode *change;
3599 if(channel->banlist.used >= MAXBANS)
3602 reply("CSMSG_BANLIST_FULL", channel->name);
3607 exists = ChannelBanExists(channel, ban);
3608 change = mod_chanmode_alloc(victimCount + 1);
3609 for(n = 0; n < victimCount; ++n)
3611 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3612 change->args[n].u.member = victims[n];
3616 change->args[n].mode = MODE_BAN;
3617 change->args[n++].u.hostmask = ban;
3621 modcmd_chanmode_announce(change);
3623 mod_chanmode_announce(chanserv, channel, change);
3624 mod_chanmode_free(change);
3626 if(exists && (action == ACTION_BAN))
3629 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3635 if(action & ACTION_KICK)
3637 char kick_reason[MAXLEN];
3638 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3640 for(n = 0; n < victimCount; n++)
3641 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3646 /* No response, since it was automated. */
3648 else if(action & ACTION_ADD_BAN)
3651 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3653 reply("CSMSG_BAN_ADDED", name, channel->name);
3655 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3656 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3657 else if(action & ACTION_BAN)
3658 reply("CSMSG_BAN_DONE", name, channel->name);
3659 else if(action & ACTION_KICK && victimCount)
3660 reply("CSMSG_KICK_DONE", name, channel->name);
3666 static CHANSERV_FUNC(cmd_kickban)
3668 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3671 static CHANSERV_FUNC(cmd_kick)
3673 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3676 static CHANSERV_FUNC(cmd_ban)
3678 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3681 static CHANSERV_FUNC(cmd_addban)
3683 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3686 static CHANSERV_FUNC(cmd_addtimedban)
3688 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3691 struct mod_chanmode *
3692 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3694 struct mod_chanmode *change;
3695 unsigned char *match;
3696 unsigned int ii, count;
3698 match = alloca(bans->used);
3701 for(ii = count = 0; ii < bans->used; ++ii)
3703 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3704 MATCH_USENICK | MATCH_VISIBLE);
3711 for(ii = count = 0; ii < bans->used; ++ii)
3713 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3720 change = mod_chanmode_alloc(count);
3721 for(ii = count = 0; ii < bans->used; ++ii)
3725 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3726 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3728 assert(count == change->argc);
3733 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3735 struct userNode *actee;
3741 /* may want to allow a comma delimited list of users... */
3742 if(!(actee = GetUserH(argv[1])))
3744 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3746 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3747 const char *accountname = argv[1] + 1;
3749 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3750 mask = strdup(banmask);
3752 else if(!is_ircmask(argv[1]))
3754 reply("MSG_NICK_UNKNOWN", argv[1]);
3759 mask = strdup(argv[1]);
3763 /* We don't sanitize the mask here because ircu
3765 if(action & ACTION_UNBAN)
3767 struct mod_chanmode *change;
3768 change = find_matching_bans(&channel->banlist, actee, mask);
3773 modcmd_chanmode_announce(change);
3774 for(ii = 0; ii < change->argc; ++ii)
3775 free((char*)change->args[ii].u.hostmask);
3776 mod_chanmode_free(change);
3781 if(action & ACTION_DEL_BAN)
3783 struct banData *ban, *next;
3785 ban = channel->channel_info->bans;
3789 for( ; ban && !user_matches_glob(actee, ban->mask,
3790 MATCH_USENICK | MATCH_VISIBLE);
3793 for( ; ban && !match_ircglobs(mask, ban->mask);
3798 del_channel_ban(ban);
3805 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3807 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3813 static CHANSERV_FUNC(cmd_unban)
3815 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3818 static CHANSERV_FUNC(cmd_delban)
3820 /* it doesn't necessarily have to remove the channel ban - may want
3821 to make that an option. */
3822 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3825 static CHANSERV_FUNC(cmd_unbanme)
3827 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3828 long flags = ACTION_UNBAN;
3830 /* remove permanent bans if the user has the proper access. */
3831 if(uData->access >= UL_MASTER)
3832 flags |= ACTION_DEL_BAN;
3834 argv[1] = user->nick;
3835 return unban_user(user, channel, 2, argv, cmd, flags);
3838 static CHANSERV_FUNC(cmd_unbanall)
3840 struct mod_chanmode *change;
3843 if(!channel->banlist.used)
3845 reply("CSMSG_NO_BANS", channel->name);
3849 change = mod_chanmode_alloc(channel->banlist.used);
3850 for(ii=0; ii<channel->banlist.used; ii++)
3852 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3853 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3855 modcmd_chanmode_announce(change);
3856 for(ii = 0; ii < change->argc; ++ii)
3857 free((char*)change->args[ii].u.hostmask);
3858 mod_chanmode_free(change);
3859 reply("CSMSG_BANS_REMOVED", channel->name);
3863 static CHANSERV_FUNC(cmd_open)
3865 struct mod_chanmode *change;
3868 change = find_matching_bans(&channel->banlist, user, NULL);
3870 change = mod_chanmode_alloc(0);
3871 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3872 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3873 && channel->channel_info->modes.modes_set)
3874 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3875 modcmd_chanmode_announce(change);
3876 reply("CSMSG_CHANNEL_OPENED", channel->name);
3877 for(ii = 0; ii < change->argc; ++ii)
3878 free((char*)change->args[ii].u.hostmask);
3879 mod_chanmode_free(change);
3883 static CHANSERV_FUNC(cmd_myaccess)
3885 static struct string_buffer sbuf;
3886 struct handle_info *target_handle;
3887 struct userData *uData;
3892 target_handle = user->handle_info;
3893 else if(!IsStaff(user))
3895 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3898 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3901 if(!oper_outranks(user, target_handle))
3904 if(!target_handle->channels)
3906 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3910 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3911 for(uData = target_handle->channels; uData; uData = uData->u_next)
3913 struct chanData *cData = uData->channel;
3915 unsigned int base_len;
3917 if(uData->access > UL_OWNER)
3919 if(uData->access == UL_OWNER)
3922 if(IsProtected(cData)
3923 && (target_handle != user->handle_info)
3924 && !GetTrueChannelAccess(cData, user->handle_info)
3925 && !IsNetworkHelper(user))
3928 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3929 base_len = sbuf.used;
3930 if(IsUserSuspended(uData))
3931 string_buffer_append(&sbuf, 's');
3932 if(IsUserAutoOp(uData))
3934 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3935 string_buffer_append(&sbuf, 'o');
3936 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3937 string_buffer_append(&sbuf, 'v');
3939 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3940 string_buffer_append(&sbuf, 'i');
3941 if(sbuf.used==base_len)
3944 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3946 string_buffer_append_string(&sbuf, ")]");
3947 string_buffer_append(&sbuf, '\0');
3948 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3952 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3954 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3960 static CHANSERV_FUNC(cmd_access)
3962 struct userNode *target;
3963 struct handle_info *target_handle;
3964 struct userData *uData;
3966 char prefix[MAXLEN];
3971 target_handle = target->handle_info;
3973 else if((target = GetUserH(argv[1])))
3975 target_handle = target->handle_info;
3977 else if(argv[1][0] == '*')
3979 if(!(target_handle = get_handle_info(argv[1]+1)))
3981 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3987 reply("MSG_NICK_UNKNOWN", argv[1]);
3991 assert(target || target_handle);
3993 if(target == chanserv)
3995 reply("CSMSG_IS_CHANSERV");
4003 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4008 reply("MSG_USER_AUTHENTICATE", target->nick);
4011 reply("MSG_AUTHENTICATE");
4017 const char *epithet = NULL, *type = NULL;
4020 epithet = chanserv_conf.irc_operator_epithet;
4021 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4023 else if(IsNetworkHelper(target))
4025 epithet = chanserv_conf.network_helper_epithet;
4026 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4028 else if(IsSupportHelper(target))
4030 epithet = chanserv_conf.support_helper_epithet;
4031 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4035 if(target_handle->epithet)
4036 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4038 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4040 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4044 sprintf(prefix, "%s", target_handle->handle);
4047 if(!channel->channel_info)
4049 reply("CSMSG_NOT_REGISTERED", channel->name);
4053 helping = HANDLE_FLAGGED(target_handle, HELPING)
4054 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4055 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4057 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4058 /* To prevent possible information leaks, only show infolines
4059 * if the requestor is in the channel or it's their own
4061 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4063 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4065 /* Likewise, only say it's suspended if the user has active
4066 * access in that channel or it's their own entry. */
4067 if(IsUserSuspended(uData)
4068 && (GetChannelUser(channel->channel_info, user->handle_info)
4069 || (user->handle_info == uData->handle)))
4071 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4076 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4083 def_list(struct listData *list)
4087 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4089 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4090 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4091 if(list->table.length == 1)
4093 msg = user_find_message(list->user, "MSG_NONE");
4094 send_message_type(4, list->user, list->bot, " %s", msg);
4099 userData_access_comp(const void *arg_a, const void *arg_b)
4101 const struct userData *a = *(struct userData**)arg_a;
4102 const struct userData *b = *(struct userData**)arg_b;
4104 if(a->access != b->access)
4105 res = b->access - a->access;
4107 res = irccasecmp(a->handle->handle, b->handle->handle);
4112 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4114 void (*send_list)(struct listData *);
4115 struct userData *uData;
4116 struct listData lData;
4117 unsigned int matches;
4121 lData.bot = cmd->parent->bot;
4122 lData.channel = channel;
4123 lData.lowest = lowest;
4124 lData.highest = highest;
4125 lData.search = (argc > 1) ? argv[1] : NULL;
4126 send_list = def_list;
4128 if(user->handle_info)
4130 switch(user->handle_info->userlist_style)
4132 case HI_STYLE_DEF: send_list = def_list; break;
4133 case HI_STYLE_ZOOT: send_list = def_list; break;
4137 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4139 for(uData = channel->channel_info->users; uData; uData = uData->next)
4141 if((uData->access < lowest)
4142 || (uData->access > highest)
4143 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4145 lData.users[matches++] = uData;
4147 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4149 lData.table.length = matches+1;
4150 lData.table.width = 4;
4151 lData.table.flags = TABLE_NO_FREE;
4152 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4153 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4154 lData.table.contents[0] = ary;
4157 ary[2] = "Last Seen";
4159 for(matches = 1; matches < lData.table.length; ++matches)
4161 char seen[INTERVALLEN];
4163 uData = lData.users[matches-1];
4164 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4165 lData.table.contents[matches] = ary;
4166 ary[0] = strtab(uData->access);
4167 ary[1] = uData->handle->handle;
4170 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4172 else if(!uData->seen)
4175 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4176 ary[2] = strdup(ary[2]);
4177 if(IsUserSuspended(uData))
4178 ary[3] = "Suspended";
4179 else if(HANDLE_FLAGGED(uData->handle, OPER))
4180 ary[3] = "Operator";
4181 else if(HANDLE_FLAGGED(uData->handle, HELPING))
4183 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4185 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4186 ary[3] = "Vacation";
4187 else if(HANDLE_FLAGGED(uData->handle, BOT))
4193 for(matches = 1; matches < lData.table.length; ++matches)
4195 free((char*)lData.table.contents[matches][2]);
4196 free(lData.table.contents[matches]);
4198 free(lData.table.contents[0]);
4199 free(lData.table.contents);
4200 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4204 static CHANSERV_FUNC(cmd_users)
4206 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4209 static CHANSERV_FUNC(cmd_wlist)
4211 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4214 static CHANSERV_FUNC(cmd_clist)
4216 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4219 static CHANSERV_FUNC(cmd_mlist)
4221 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4224 static CHANSERV_FUNC(cmd_olist)
4226 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4229 static CHANSERV_FUNC(cmd_plist)
4231 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4234 static CHANSERV_FUNC(cmd_bans)
4236 struct userNode *search_u = NULL;
4237 struct helpfile_table tbl;
4238 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4239 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4240 const char *msg_never, *triggered, *expires;
4241 struct banData *ban, **bans;
4245 else if(strchr(search = argv[1], '!'))
4248 search_wilds = search[strcspn(search, "?*")];
4250 else if(!(search_u = GetUserH(search)))
4251 reply("MSG_NICK_UNKNOWN", search);
4253 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4255 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4259 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4264 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4267 bans[matches++] = ban;
4272 tbl.length = matches + 1;
4273 tbl.width = 4 + timed;
4275 tbl.flags = TABLE_NO_FREE;
4276 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4277 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4278 tbl.contents[0][0] = "Mask";
4279 tbl.contents[0][1] = "Set By";
4280 tbl.contents[0][2] = "Triggered";
4283 tbl.contents[0][3] = "Expires";
4284 tbl.contents[0][4] = "Reason";
4287 tbl.contents[0][3] = "Reason";
4290 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4292 free(tbl.contents[0]);
4297 msg_never = user_find_message(user, "MSG_NEVER");
4298 for(ii = 0; ii < matches; )
4304 else if(ban->expires)
4305 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4307 expires = msg_never;
4310 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4312 triggered = msg_never;
4314 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4315 tbl.contents[ii][0] = ban->mask;
4316 tbl.contents[ii][1] = ban->owner;
4317 tbl.contents[ii][2] = strdup(triggered);
4320 tbl.contents[ii][3] = strdup(expires);
4321 tbl.contents[ii][4] = ban->reason;
4324 tbl.contents[ii][3] = ban->reason;
4326 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4327 reply("MSG_MATCH_COUNT", matches);
4328 for(ii = 1; ii < tbl.length; ++ii)
4330 free((char*)tbl.contents[ii][2]);
4332 free((char*)tbl.contents[ii][3]);
4333 free(tbl.contents[ii]);
4335 free(tbl.contents[0]);
4341 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4343 struct chanData *cData = channel->channel_info;
4344 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4346 if(cData->topic_mask)
4348 if(cData->flags & CHANNEL_ADVTOPIC)
4350 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4351 char topicmask[TOPICLEN];
4352 int skipnum, topicpos = 0;
4353 char *ptr = cData->topic_mask;
4354 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4357 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4359 topicmask[topicpos++] = '*';
4361 topicmask[topicpos++] = *ptr;
4364 topicmask[topicpos++] = *ptr;
4368 topicmask[topicpos] = 0;
4369 return !match_ircglob(new_topic, topicmask);
4372 return !match_ircglob(new_topic, cData->topic_mask);
4374 else if(cData->topic)
4375 return irccasecmp(new_topic, cData->topic);
4380 static CHANSERV_FUNC(cmd_topic)
4382 struct chanData *cData;
4385 cData = channel->channel_info;
4390 SetChannelTopic(channel, chanserv, cData->topic, 1);
4391 reply("CSMSG_TOPIC_SET", cData->topic);
4395 reply("CSMSG_NO_TOPIC", channel->name);
4399 topic = unsplit_string(argv + 1, argc - 1, NULL);
4400 /* If they say "!topic *", use an empty topic. */
4401 if((topic[0] == '*') && (topic[1] == 0))
4403 if(bad_topic(channel, user, topic))
4405 char *topic_mask = cData->topic_mask;
4408 char new_topic[TOPICLEN+1], tchar;
4409 int pos=0, starpos=-1, dpos=0, len;
4411 if(cData->flags & CHANNEL_ADVTOPIC)
4413 //first check if there is a leading 'modifier id'
4414 int advtopic_index = 0;
4417 for(; topic[pos]; pos++)
4419 if(topic[pos] == ' ')
4421 //leading number found, cut off and store value in advtopic_index
4423 advtopic_index = atoi(topic) - 1; //no zerobase
4424 topic = &topic[pos+1];
4425 /* If they say "!topic 2 *", unset advtopic id 2. */
4426 if((topic[0] == '*') && (topic[1] == 0))
4429 if(!isdigit(topic[pos]))
4432 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4435 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4438 if(cData->advtopic[advtopic_index])
4439 free(cData->advtopic[advtopic_index]);
4440 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4441 char *ptr = topic_mask;
4442 while(*ptr && (dpos <= TOPICLEN))
4448 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4449 numbuf[numpos++] = *ptr;
4452 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4454 new_topic[dpos++] = *ptr; //is % again
4457 advtopic_index--; //no zero base
4458 if(!cData->advtopic[advtopic_index])
4459 break; //just leave it empty
4460 len = strlen(cData->advtopic[advtopic_index]);
4461 if((dpos + len) > TOPICLEN)
4462 len = TOPICLEN + 1 - dpos;
4463 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4467 ptr++; /* and fall through */
4470 new_topic[dpos++] = *ptr;
4476 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4483 len = strlen(topic);
4484 if((dpos + len) > TOPICLEN)
4485 len = TOPICLEN + 1 - dpos;
4486 memcpy(new_topic+dpos, topic, len);
4490 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4491 default: new_topic[dpos++] = tchar; break;
4494 if((dpos > TOPICLEN) || tchar)
4497 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4498 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4502 new_topic[dpos] = 0;
4503 SetChannelTopic(channel, chanserv, new_topic, 1);
4505 reply("CSMSG_TOPIC_LOCKED", channel->name);
4510 SetChannelTopic(channel, chanserv, topic, 1);
4512 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4514 /* Grab the topic and save it as the default topic. */
4516 cData->topic = strdup(channel->topic);
4522 static CHANSERV_FUNC(cmd_mode)
4524 struct userData *uData;
4525 struct mod_chanmode *change;
4531 change = &channel->channel_info->modes;
4532 if(change->modes_set || change->modes_clear) {
4533 modcmd_chanmode_announce(change);
4534 reply("CSMSG_DEFAULTED_MODES", channel->name);
4536 reply("CSMSG_NO_MODES", channel->name);
4540 uData = GetChannelUser(channel->channel_info, user->handle_info);
4542 base_oplevel = MAXOPLEVEL;
4543 else if (uData->access >= UL_OWNER)
4546 base_oplevel = 1 + UL_OWNER - uData->access;
4547 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4550 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4554 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4555 && mode_lock_violated(&channel->channel_info->modes, change))
4558 mod_chanmode_format(&channel->channel_info->modes, modes);
4559 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4563 modcmd_chanmode_announce(change);
4564 mod_chanmode_format(change, fmt);
4565 mod_chanmode_free(change);
4566 reply("CSMSG_MODES_SET", fmt);
4571 chanserv_del_invite_mark(void *data)
4573 struct ChanUser *chanuser = data;
4574 struct chanNode *channel = chanuser->chan;
4576 if(!channel) return;
4577 for(i = 0; i < channel->invited.used; i++)
4579 if(channel->invited.list[i] == chanuser->user) {
4580 userList_remove(&channel->invited, chanuser->user);
4586 static CHANSERV_FUNC(cmd_invite)
4588 struct userNode *invite;
4589 struct ChanUser *chanuser;
4594 if(!(invite = GetUserH(argv[1])))
4596 reply("MSG_NICK_UNKNOWN", argv[1]);
4603 if(GetUserMode(channel, invite))
4605 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4609 for(i = 0; i < channel->invited.used; i++)
4611 if(channel->invited.list[i] == invite) {
4612 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4621 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4622 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4625 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4627 irc_invite(chanserv, invite, channel);
4629 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4631 userList_append(&channel->invited, invite);
4632 chanuser = calloc(1, sizeof(*chanuser));
4633 chanuser->user=invite;
4634 chanuser->chan=channel;
4635 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4640 static CHANSERV_FUNC(cmd_inviteme)
4642 if(GetUserMode(channel, user))
4644 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4647 if(channel->channel_info
4648 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4650 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4653 irc_invite(cmd->parent->bot, user, channel);
4657 static CHANSERV_FUNC(cmd_invitemeall)
4659 struct handle_info *target = user->handle_info;
4660 struct userData *uData;
4662 if(!target->channels)
4664 reply("CSMSG_SQUAT_ACCESS", target->handle);
4668 for(uData = target->channels; uData; uData = uData->u_next)
4670 struct chanData *cData = uData->channel;
4671 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4673 irc_invite(cmd->parent->bot, user, cData->channel);
4680 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4683 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4685 /* We display things based on two dimensions:
4686 * - Issue time: present or absent
4687 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4688 * (in order of precedence, so something both expired and revoked
4689 * only counts as revoked)
4691 combo = (suspended->issued ? 4 : 0)
4692 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4694 case 0: /* no issue time, indefinite expiration */
4695 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4697 case 1: /* no issue time, expires in future */
4698 intervalString(buf1, suspended->expires-now, user->handle_info);
4699 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4701 case 2: /* no issue time, expired */
4702 intervalString(buf1, now-suspended->expires, user->handle_info);
4703 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4705 case 3: /* no issue time, revoked */
4706 intervalString(buf1, now-suspended->revoked, user->handle_info);
4707 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4709 case 4: /* issue time set, indefinite expiration */
4710 intervalString(buf1, now-suspended->issued, user->handle_info);
4711 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4713 case 5: /* issue time set, expires in future */
4714 intervalString(buf1, now-suspended->issued, user->handle_info);
4715 intervalString(buf2, suspended->expires-now, user->handle_info);
4716 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4718 case 6: /* issue time set, expired */
4719 intervalString(buf1, now-suspended->issued, user->handle_info);
4720 intervalString(buf2, now-suspended->expires, user->handle_info);
4721 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4723 case 7: /* issue time set, revoked */
4724 intervalString(buf1, now-suspended->issued, user->handle_info);
4725 intervalString(buf2, now-suspended->revoked, user->handle_info);
4726 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4729 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4735 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4738 const char *fmt = "%a %b %d %H:%M %Y";
4739 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4741 if(giveownership->staff_issuer)
4743 if(giveownership->reason)
4744 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4745 giveownership->target, giveownership->target_access,
4746 giveownership->staff_issuer, buf, giveownership->reason);
4748 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4749 giveownership->target, giveownership->target_access,
4750 giveownership->staff_issuer, buf);
4754 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4758 static CHANSERV_FUNC(cmd_info)
4760 char modes[MAXLEN], buffer[INTERVALLEN];
4761 struct userData *uData, *owner;
4762 struct chanData *cData;
4763 struct do_not_register *dnr;
4768 cData = channel->channel_info;
4769 reply("CSMSG_CHANNEL_INFO", channel->name);
4771 uData = GetChannelUser(cData, user->handle_info);
4772 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4774 mod_chanmode_format(&cData->modes, modes);
4775 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4776 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4779 for(it = dict_first(cData->notes); it; it = iter_next(it))
4783 note = iter_data(it);
4784 if(!note_type_visible_to_user(cData, note->type, user))
4787 padding = PADLEN - 1 - strlen(iter_key(it));
4788 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4791 if(cData->max_time) {
4792 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4794 reply("CSMSG_CHANNEL_MAX", cData->max);
4796 for(owner = cData->users; owner; owner = owner->next)
4797 if(owner->access == UL_OWNER)
4798 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4799 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4800 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4801 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4803 privileged = IsStaff(user);
4805 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4806 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4807 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4809 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4810 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4812 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4814 struct suspended *suspended;
4815 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4816 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4817 show_suspension_info(cmd, user, suspended);
4819 else if(IsSuspended(cData))
4821 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4822 show_suspension_info(cmd, user, cData->suspended);
4825 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4827 struct giveownership *giveownership;
4828 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4829 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4830 show_giveownership_info(cmd, user, giveownership);
4835 static CHANSERV_FUNC(cmd_netinfo)
4837 extern unsigned long boot_time;
4838 extern unsigned long burst_length;
4839 char interval[INTERVALLEN];
4841 reply("CSMSG_NETWORK_INFO");
4842 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4843 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4844 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4845 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4846 reply("CSMSG_NETWORK_BANS", banCount);
4847 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4848 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4849 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4854 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4856 struct helpfile_table table;
4858 struct userNode *user;
4863 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4864 table.contents = alloca(list->used*sizeof(*table.contents));
4865 for(nn=0; nn<list->used; nn++)
4867 user = list->list[nn];
4868 if(user->modes & skip_flags)
4874 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4877 nick = alloca(strlen(user->nick)+3);
4878 sprintf(nick, "(%s)", user->nick);
4882 table.contents[table.length][0] = nick;
4885 table_send(chanserv, to->nick, 0, NULL, table);
4888 static CHANSERV_FUNC(cmd_ircops)
4890 reply("CSMSG_STAFF_OPERS");
4891 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4895 static CHANSERV_FUNC(cmd_helpers)
4897 reply("CSMSG_STAFF_HELPERS");
4898 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4902 static CHANSERV_FUNC(cmd_staff)
4904 reply("CSMSG_NETWORK_STAFF");
4905 cmd_ircops(CSFUNC_ARGS);
4906 cmd_helpers(CSFUNC_ARGS);
4910 static CHANSERV_FUNC(cmd_peek)
4912 struct modeNode *mn;
4913 char modes[MODELEN];
4915 struct helpfile_table table;
4916 int opcount = 0, voicecount = 0, srvcount = 0;
4918 irc_make_chanmode(channel, modes);
4920 reply("CSMSG_PEEK_INFO", channel->name);
4921 reply("CSMSG_PEEK_TOPIC", channel->topic);
4922 reply("CSMSG_PEEK_MODES", modes);
4926 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4927 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4928 for(n = 0; n < channel->members.used; n++)
4930 mn = channel->members.list[n];
4931 if(IsLocal(mn->user))
4933 else if(mn->modes & MODE_CHANOP)
4935 else if(mn->modes & MODE_VOICE)
4938 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4940 table.contents[table.length] = alloca(sizeof(**table.contents));
4941 table.contents[table.length][0] = mn->user->nick;
4945 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4946 (channel->members.used - opcount - voicecount - srvcount));
4950 reply("CSMSG_PEEK_OPS");
4951 table_send(chanserv, user->nick, 0, NULL, table);
4954 reply("CSMSG_PEEK_NO_OPS");
4958 static MODCMD_FUNC(cmd_wipeinfo)
4960 struct handle_info *victim;
4961 struct userData *ud, *actor, *real_actor;
4962 unsigned int override = 0;
4965 actor = GetChannelUser(channel->channel_info, user->handle_info);
4966 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4967 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4969 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4971 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4974 if((ud->access >= actor->access) && (ud != actor))
4976 reply("MSG_USER_OUTRANKED", victim->handle);
4979 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4980 override = CMD_LOG_OVERRIDE;
4984 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4985 return 1 | override;
4988 static CHANSERV_FUNC(cmd_resync)
4990 struct mod_chanmode *changes;
4991 struct chanData *cData = channel->channel_info;
4992 unsigned int ii, used;
4994 changes = mod_chanmode_alloc(channel->members.used * 2);
4995 for(ii = used = 0; ii < channel->members.used; ++ii)
4997 struct modeNode *mn = channel->members.list[ii];
4998 struct userData *uData;
5000 if(IsService(mn->user))
5003 uData = GetChannelAccess(cData, mn->user->handle_info);
5004 if(!cData->lvlOpts[lvlGiveOps]
5005 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
5007 if(!(mn->modes & MODE_CHANOP))
5009 if(!uData || IsUserAutoOp(uData))
5011 changes->args[used].mode = MODE_CHANOP;
5012 changes->args[used++].u.member = mn;
5013 if(!(mn->modes & MODE_VOICE))
5015 changes->args[used].mode = MODE_VOICE;
5016 changes->args[used++].u.member = mn;
5021 else if(!cData->lvlOpts[lvlGiveVoice]
5022 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5024 if(mn->modes & MODE_CHANOP)
5026 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5027 changes->args[used++].u.member = mn;
5029 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5031 changes->args[used].mode = MODE_VOICE;
5032 changes->args[used++].u.member = mn;
5039 changes->args[used].mode = MODE_REMOVE | mn->modes;
5040 changes->args[used++].u.member = mn;
5044 changes->argc = used;
5045 modcmd_chanmode_announce(changes);
5046 mod_chanmode_free(changes);
5047 reply("CSMSG_RESYNCED_USERS", channel->name);
5051 static CHANSERV_FUNC(cmd_seen)
5053 struct userData *uData;
5054 struct handle_info *handle;
5055 char seen[INTERVALLEN];
5059 if(!irccasecmp(argv[1], chanserv->nick))
5061 reply("CSMSG_IS_CHANSERV");
5065 if(!(handle = get_handle_info(argv[1])))
5067 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5071 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5073 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5078 reply("CSMSG_USER_PRESENT", handle->handle);
5079 else if(uData->seen)
5080 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5082 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5084 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5085 reply("CSMSG_USER_VACATION", handle->handle);
5090 static MODCMD_FUNC(cmd_names)
5092 struct userNode *targ;
5093 struct userData *targData;
5094 unsigned int ii, pos;
5097 for(ii=pos=0; ii<channel->members.used; ++ii)
5099 targ = channel->members.list[ii]->user;
5100 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5103 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5106 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5110 if(IsUserSuspended(targData))
5112 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5115 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5116 reply("CSMSG_END_NAMES", channel->name);
5121 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5123 switch(ntype->visible_type)
5125 case NOTE_VIS_ALL: return 1;
5126 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5127 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5132 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5134 struct userData *uData;
5136 switch(ntype->set_access_type)
5138 case NOTE_SET_CHANNEL_ACCESS:
5139 if(!user->handle_info)
5141 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5143 return uData->access >= ntype->set_access.min_ulevel;
5144 case NOTE_SET_CHANNEL_SETTER:
5145 return check_user_level(channel, user, lvlSetters, 1, 0);
5146 case NOTE_SET_PRIVILEGED: default:
5147 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5151 static CHANSERV_FUNC(cmd_note)
5153 struct chanData *cData;
5155 struct note_type *ntype;
5157 cData = channel->channel_info;
5160 reply("CSMSG_NOT_REGISTERED", channel->name);
5164 /* If no arguments, show all visible notes for the channel. */
5170 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5172 note = iter_data(it);
5173 if(!note_type_visible_to_user(cData, note->type, user))
5176 reply("CSMSG_NOTELIST_HEADER", channel->name);
5177 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5180 reply("CSMSG_NOTELIST_END", channel->name);
5182 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5184 /* If one argument, show the named note. */
5187 if((note = dict_find(cData->notes, argv[1], NULL))
5188 && note_type_visible_to_user(cData, note->type, user))
5190 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5192 else if((ntype = dict_find(note_types, argv[1], NULL))
5193 && note_type_visible_to_user(NULL, ntype, user))
5195 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5200 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5204 /* Assume they're trying to set a note. */
5208 ntype = dict_find(note_types, argv[1], NULL);
5211 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5214 else if(note_type_settable_by_user(channel, ntype, user))
5216 note_text = unsplit_string(argv+2, argc-2, NULL);
5217 if((note = dict_find(cData->notes, argv[1], NULL)))
5218 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5219 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5220 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5222 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5224 /* The note is viewable to staff only, so return 0
5225 to keep the invocation from getting logged (or
5226 regular users can see it in !events). */
5232 reply("CSMSG_NO_ACCESS");
5239 static CHANSERV_FUNC(cmd_delnote)
5244 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5245 || !note_type_settable_by_user(channel, note->type, user))
5247 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5250 dict_remove(channel->channel_info->notes, note->type->name);
5251 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5255 static CHANSERV_FUNC(cmd_events)
5257 struct logSearch discrim;
5258 struct logReport report;
5259 unsigned int matches, limit;
5261 limit = (argc > 1) ? atoi(argv[1]) : 10;
5262 if(limit < 1 || limit > 200)
5265 memset(&discrim, 0, sizeof(discrim));
5266 discrim.masks.bot = chanserv;
5267 discrim.masks.channel_name = channel->name;
5269 discrim.masks.command = argv[2];
5270 discrim.limit = limit;
5271 discrim.max_time = INT_MAX;
5272 discrim.severities = 1 << LOG_COMMAND;
5273 report.reporter = chanserv;
5275 reply("CSMSG_EVENT_SEARCH_RESULTS");
5276 matches = log_entry_search(&discrim, log_report_entry, &report);
5278 reply("MSG_MATCH_COUNT", matches);
5280 reply("MSG_NO_MATCHES");
5284 static CHANSERV_FUNC(cmd_say)
5290 msg = unsplit_string(argv + 1, argc - 1, NULL);
5291 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5293 else if(*argv[1] == '*' && argv[1][1] != '\0')
5295 struct handle_info *hi;
5296 struct userNode *authed;
5299 msg = unsplit_string(argv + 2, argc - 2, NULL);
5301 if (!(hi = get_handle_info(argv[1] + 1)))
5303 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5307 for (authed = hi->users; authed; authed = authed->next_authed)
5308 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5310 else if(GetUserH(argv[1]))
5313 msg = unsplit_string(argv + 2, argc - 2, NULL);
5314 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5318 reply("MSG_NOT_TARGET_NAME");
5324 static CHANSERV_FUNC(cmd_emote)
5330 /* CTCP is so annoying. */
5331 msg = unsplit_string(argv + 1, argc - 1, NULL);
5332 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5334 else if(*argv[1] == '*' && argv[1][1] != '\0')
5336 struct handle_info *hi;
5337 struct userNode *authed;
5340 msg = unsplit_string(argv + 2, argc - 2, NULL);
5342 if (!(hi = get_handle_info(argv[1] + 1)))
5344 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5348 for (authed = hi->users; authed; authed = authed->next_authed)
5349 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5351 else if(GetUserH(argv[1]))
5353 msg = unsplit_string(argv + 2, argc - 2, NULL);
5354 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5358 reply("MSG_NOT_TARGET_NAME");
5364 struct channelList *
5365 chanserv_support_channels(void)
5367 return &chanserv_conf.support_channels;
5370 static CHANSERV_FUNC(cmd_expire)
5372 int channel_count = registered_channels;
5373 expire_channels(chanserv);
5374 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5379 chanserv_expire_suspension(void *data)
5381 struct suspended *suspended = data;
5382 struct chanNode *channel;
5385 /* Update the channel registration data structure. */
5386 if(!suspended->expires || (now < suspended->expires))
5387 suspended->revoked = now;
5388 channel = suspended->cData->channel;
5389 suspended->cData->channel = channel;
5390 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5392 /* If appropriate, re-join ChanServ to the channel. */
5393 if(!IsOffChannel(suspended->cData))
5395 spamserv_cs_suspend(channel, 0, 0, NULL);
5396 ss_cs_join_channel(channel, 1);
5399 /* Mark everyone currently in the channel as present. */
5400 for(ii = 0; ii < channel->members.used; ++ii)
5402 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5411 static CHANSERV_FUNC(cmd_csuspend)
5413 struct suspended *suspended;
5414 char reason[MAXLEN];
5415 unsigned long expiry, duration;
5416 struct userData *uData;
5420 if(IsProtected(channel->channel_info))
5422 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5426 if(argv[1][0] == '!')
5428 else if(IsSuspended(channel->channel_info))
5430 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5431 show_suspension_info(cmd, user, channel->channel_info->suspended);
5435 if(!strcmp(argv[1], "0"))
5437 else if((duration = ParseInterval(argv[1])))
5438 expiry = now + duration;
5441 reply("MSG_INVALID_DURATION", argv[1]);
5445 unsplit_string(argv + 2, argc - 2, reason);
5447 suspended = calloc(1, sizeof(*suspended));
5448 suspended->revoked = 0;
5449 suspended->issued = now;
5450 suspended->suspender = strdup(user->handle_info->handle);
5451 suspended->expires = expiry;
5452 suspended->reason = strdup(reason);
5453 suspended->cData = channel->channel_info;
5454 suspended->previous = suspended->cData->suspended;
5455 suspended->cData->suspended = suspended;
5457 if(suspended->expires)
5458 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5460 if(IsSuspended(channel->channel_info))
5462 suspended->previous->revoked = now;
5463 if(suspended->previous->expires)
5464 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5465 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5466 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5470 /* Mark all users in channel as absent. */
5471 for(uData = channel->channel_info->users; uData; uData = uData->next)
5480 /* Mark the channel as suspended, then part. */
5481 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5482 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5483 DelChannelUser(chanserv, channel, suspended->reason, 0);
5484 reply("CSMSG_SUSPENDED", channel->name);
5485 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5486 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5491 static CHANSERV_FUNC(cmd_cunsuspend)
5493 struct suspended *suspended;
5494 char message[MAXLEN];
5496 if(!IsSuspended(channel->channel_info))
5498 reply("CSMSG_NOT_SUSPENDED", channel->name);
5502 suspended = channel->channel_info->suspended;
5504 /* Expire the suspension and join ChanServ to the channel. */
5505 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5506 chanserv_expire_suspension(suspended);
5507 reply("CSMSG_UNSUSPENDED", channel->name);
5508 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5509 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5513 typedef struct chanservSearch
5518 unsigned long unvisited;
5519 unsigned long registered;
5521 unsigned long flags;
5525 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5528 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5533 search = malloc(sizeof(struct chanservSearch));
5534 memset(search, 0, sizeof(*search));
5537 for(i = 0; i < argc; i++)
5539 /* Assume all criteria require arguments. */
5542 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5546 if(!irccasecmp(argv[i], "name"))
5547 search->name = argv[++i];
5548 else if(!irccasecmp(argv[i], "registrar"))
5549 search->registrar = argv[++i];
5550 else if(!irccasecmp(argv[i], "unvisited"))
5551 search->unvisited = ParseInterval(argv[++i]);
5552 else if(!irccasecmp(argv[i], "registered"))
5553 search->registered = ParseInterval(argv[++i]);
5554 else if(!irccasecmp(argv[i], "flags"))
5557 if(!irccasecmp(argv[i], "nodelete"))
5558 search->flags |= CHANNEL_NODELETE;
5559 else if(!irccasecmp(argv[i], "suspended"))
5560 search->flags |= CHANNEL_SUSPENDED;
5561 else if(!irccasecmp(argv[i], "unreviewed"))
5562 search->flags |= CHANNEL_UNREVIEWED;
5565 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5569 else if(!irccasecmp(argv[i], "limit"))
5570 search->limit = strtoul(argv[++i], NULL, 10);
5573 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5578 if(search->name && !strcmp(search->name, "*"))
5580 if(search->registrar && !strcmp(search->registrar, "*"))
5581 search->registrar = 0;
5590 chanserv_channel_match(struct chanData *channel, search_t search)
5592 const char *name = channel->channel->name;
5593 if((search->name && !match_ircglob(name, search->name)) ||
5594 (search->registrar && !channel->registrar) ||
5595 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5596 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5597 (search->registered && (now - channel->registered) > search->registered) ||
5598 (search->flags && ((search->flags & channel->flags) != search->flags)))
5605 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5607 struct chanData *channel;
5608 unsigned int matches = 0;
5610 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5612 if(!chanserv_channel_match(channel, search))
5622 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5627 search_print(struct chanData *channel, void *data)
5629 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5632 static CHANSERV_FUNC(cmd_search)
5635 unsigned int matches;
5636 channel_search_func action;
5640 if(!irccasecmp(argv[1], "count"))
5641 action = search_count;
5642 else if(!irccasecmp(argv[1], "print"))
5643 action = search_print;
5646 reply("CSMSG_ACTION_INVALID", argv[1]);
5650 search = chanserv_search_create(user, argc - 2, argv + 2);
5654 if(action == search_count)
5655 search->limit = INT_MAX;
5657 if(action == search_print)
5658 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5660 matches = chanserv_channel_search(search, action, user);
5663 reply("MSG_MATCH_COUNT", matches);
5665 reply("MSG_NO_MATCHES");
5671 static CHANSERV_FUNC(cmd_unvisited)
5673 struct chanData *cData;
5674 unsigned long interval = chanserv_conf.channel_expire_delay;
5675 char buffer[INTERVALLEN];
5676 unsigned int limit = 25, matches = 0;
5680 interval = ParseInterval(argv[1]);
5682 limit = atoi(argv[2]);
5685 intervalString(buffer, interval, user->handle_info);
5686 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5688 for(cData = channelList; cData && matches < limit; cData = cData->next)
5690 if((now - cData->visited) < interval)
5693 intervalString(buffer, now - cData->visited, user->handle_info);
5694 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5701 static MODCMD_FUNC(chan_opt_defaulttopic)
5707 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5709 reply("CSMSG_TOPIC_LOCKED", channel->name);
5713 topic = unsplit_string(argv+1, argc-1, NULL);
5715 free(channel->channel_info->topic);
5716 if(topic[0] == '*' && topic[1] == 0)
5718 topic = channel->channel_info->topic = NULL;
5722 topic = channel->channel_info->topic = strdup(topic);
5723 if(channel->channel_info->topic_mask
5724 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5725 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5727 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5730 if(channel->channel_info->topic)
5731 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5733 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5737 static MODCMD_FUNC(chan_opt_topicmask)
5741 struct chanData *cData = channel->channel_info;
5744 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5746 reply("CSMSG_TOPIC_LOCKED", channel->name);
5750 mask = unsplit_string(argv+1, argc-1, NULL);
5752 if(cData->topic_mask)
5753 free(cData->topic_mask);
5754 if(mask[0] == '*' && mask[1] == 0)
5756 cData->topic_mask = 0;
5760 cData->topic_mask = strdup(mask);
5762 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5763 else if(!match_ircglob(cData->topic, cData->topic_mask))
5764 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5768 if(channel->channel_info->topic_mask)
5769 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5771 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5775 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5779 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5783 if(greeting[0] == '*' && greeting[1] == 0)
5787 unsigned int length = strlen(greeting);
5788 if(length > chanserv_conf.greeting_length)
5790 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5793 *data = strdup(greeting);
5802 reply(name, user_find_message(user, "MSG_NONE"));
5806 static MODCMD_FUNC(chan_opt_greeting)
5808 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5811 static MODCMD_FUNC(chan_opt_usergreeting)
5813 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5816 static MODCMD_FUNC(chan_opt_modes)
5818 struct mod_chanmode *new_modes;
5823 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5825 reply("CSMSG_NO_ACCESS");
5828 if(argv[1][0] == '*' && argv[1][1] == 0)
5830 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5832 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)))
5834 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5837 else if(new_modes->argc > 1)
5839 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5840 mod_chanmode_free(new_modes);
5845 channel->channel_info->modes = *new_modes;
5846 modcmd_chanmode_announce(new_modes);
5847 mod_chanmode_free(new_modes);
5851 mod_chanmode_format(&channel->channel_info->modes, modes);
5853 reply("CSMSG_SET_MODES", modes);
5855 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5859 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5861 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5863 struct chanData *cData = channel->channel_info;
5868 /* Set flag according to value. */
5869 if(enabled_string(argv[1]))
5871 cData->flags |= mask;
5874 else if(disabled_string(argv[1]))
5876 cData->flags &= ~mask;
5881 reply("MSG_INVALID_BINARY", argv[1]);
5887 /* Find current option value. */
5888 value = (cData->flags & mask) ? 1 : 0;
5892 reply(name, user_find_message(user, "MSG_ON"));
5894 reply(name, user_find_message(user, "MSG_OFF"));
5898 static MODCMD_FUNC(chan_opt_nodelete)
5900 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5902 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5906 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5909 static MODCMD_FUNC(chan_opt_dynlimit)
5911 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5914 static MODCMD_FUNC(chan_opt_advtopic)
5916 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5919 static MODCMD_FUNC(chan_opt_offchannel)
5921 struct chanData *cData = channel->channel_info;
5926 /* Set flag according to value. */
5927 if(enabled_string(argv[1]))
5929 if(!IsOffChannel(cData))
5930 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5931 cData->flags |= CHANNEL_OFFCHANNEL;
5934 else if(disabled_string(argv[1]))
5936 if(IsOffChannel(cData))
5938 struct mod_chanmode change;
5939 mod_chanmode_init(&change);
5941 change.args[0].mode = MODE_CHANOP;
5942 change.args[0].u.member = AddChannelUser(chanserv, channel);
5943 mod_chanmode_announce(chanserv, channel, &change);
5945 cData->flags &= ~CHANNEL_OFFCHANNEL;
5950 reply("MSG_INVALID_BINARY", argv[1]);
5956 /* Find current option value. */
5957 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5961 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5963 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5967 static MODCMD_FUNC(chan_opt_unreviewed)
5969 struct chanData *cData = channel->channel_info;
5970 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5976 /* The two directions can have different ACLs. */
5977 if(enabled_string(argv[1]))
5979 else if(disabled_string(argv[1]))
5983 reply("MSG_INVALID_BINARY", argv[1]);
5987 if (new_value != value)
5989 struct svccmd *subcmd;
5990 char subcmd_name[32];
5992 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5993 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5996 reply("MSG_COMMAND_DISABLED", subcmd_name);
5999 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6003 cData->flags |= CHANNEL_UNREVIEWED;
6006 free(cData->registrar);
6007 cData->registrar = strdup(user->handle_info->handle);
6008 cData->flags &= ~CHANNEL_UNREVIEWED;
6015 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6017 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6021 static MODCMD_FUNC(chan_opt_defaults)
6023 struct userData *uData;
6024 struct chanData *cData;
6025 const char *confirm;
6026 enum levelOption lvlOpt;
6027 enum charOption chOpt;
6029 cData = channel->channel_info;
6030 uData = GetChannelUser(cData, user->handle_info);
6031 if(!uData || (uData->access < UL_OWNER))
6033 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6036 confirm = make_confirmation_string(uData);
6037 if((argc < 2) || strcmp(argv[1], confirm))
6039 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6042 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6043 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6044 cData->modes = chanserv_conf.default_modes;
6045 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6046 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6047 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6048 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6049 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6054 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6056 struct chanData *cData = channel->channel_info;
6057 struct userData *uData;
6058 unsigned short value;
6062 if(!check_user_level(channel, user, option, 1, 1))
6064 reply("CSMSG_CANNOT_SET");
6067 value = user_level_from_name(argv[1], UL_OWNER+1);
6068 if(!value && strcmp(argv[1], "0"))
6070 reply("CSMSG_INVALID_ACCESS", argv[1]);
6073 uData = GetChannelUser(cData, user->handle_info);
6074 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6076 reply("CSMSG_BAD_SETLEVEL");
6082 if(value > cData->lvlOpts[lvlGiveOps])
6084 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6089 if(value < cData->lvlOpts[lvlGiveVoice])
6091 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6096 /* This test only applies to owners, since non-owners
6097 * trying to set an option to above their level get caught
6098 * by the CSMSG_BAD_SETLEVEL test above.
6100 if(value > uData->access)
6102 reply("CSMSG_BAD_SETTERS");
6109 cData->lvlOpts[option] = value;
6111 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6115 static MODCMD_FUNC(chan_opt_enfops)
6117 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6120 static MODCMD_FUNC(chan_opt_giveops)
6122 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6125 static MODCMD_FUNC(chan_opt_enfmodes)
6127 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6130 static MODCMD_FUNC(chan_opt_enftopic)
6132 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6135 static MODCMD_FUNC(chan_opt_pubcmd)
6137 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6140 static MODCMD_FUNC(chan_opt_setters)
6142 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6145 static MODCMD_FUNC(chan_opt_ctcpusers)
6147 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6150 static MODCMD_FUNC(chan_opt_userinfo)
6152 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6155 static MODCMD_FUNC(chan_opt_givevoice)
6157 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6160 static MODCMD_FUNC(chan_opt_topicsnarf)
6162 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6165 static MODCMD_FUNC(chan_opt_vote)
6167 return channel_level_option(lvlVote, CSFUNC_ARGS);
6170 static MODCMD_FUNC(chan_opt_inviteme)
6172 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6176 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6178 struct chanData *cData = channel->channel_info;
6179 int count = charOptions[option].count, idx;
6183 idx = atoi(argv[1]);
6185 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6187 reply("CSMSG_INVALID_NUMERIC", idx);
6188 /* Show possible values. */
6189 for(idx = 0; idx < count; idx++)
6190 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6194 cData->chOpts[option] = charOptions[option].values[idx].value;
6198 /* Find current option value. */
6201 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6205 /* Somehow, the option value is corrupt; reset it to the default. */
6206 cData->chOpts[option] = charOptions[option].default_value;
6211 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6215 static MODCMD_FUNC(chan_opt_protect)
6217 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6220 static MODCMD_FUNC(chan_opt_toys)
6222 return channel_multiple_option(chToys, CSFUNC_ARGS);
6225 static MODCMD_FUNC(chan_opt_ctcpreaction)
6227 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6230 static MODCMD_FUNC(chan_opt_topicrefresh)
6232 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6235 static struct svccmd_list set_shows_list;
6238 handle_svccmd_unbind(struct svccmd *target) {
6240 for(ii=0; ii<set_shows_list.used; ++ii)
6241 if(target == set_shows_list.list[ii])
6242 set_shows_list.used = 0;
6245 static CHANSERV_FUNC(cmd_set)
6247 struct svccmd *subcmd;
6251 /* Check if we need to (re-)initialize set_shows_list. */
6252 if(!set_shows_list.used)
6254 if(!set_shows_list.size)
6256 set_shows_list.size = chanserv_conf.set_shows->used;
6257 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6259 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6261 const char *name = chanserv_conf.set_shows->list[ii];
6262 sprintf(buf, "%s %s", argv[0], name);
6263 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6266 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6269 svccmd_list_append(&set_shows_list, subcmd);
6275 reply("CSMSG_CHANNEL_OPTIONS");
6276 for(ii = 0; ii < set_shows_list.used; ii++)
6278 subcmd = set_shows_list.list[ii];
6279 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6284 sprintf(buf, "%s %s", argv[0], argv[1]);
6285 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6288 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6291 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6293 reply("CSMSG_NO_ACCESS");
6299 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6303 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6305 struct userData *uData;
6307 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6310 reply("CSMSG_NOT_USER", channel->name);
6316 /* Just show current option value. */
6318 else if(enabled_string(argv[1]))
6320 uData->flags |= mask;
6322 else if(disabled_string(argv[1]))
6324 uData->flags &= ~mask;
6328 reply("MSG_INVALID_BINARY", argv[1]);
6332 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6336 static MODCMD_FUNC(user_opt_noautoop)
6338 struct userData *uData;
6340 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6343 reply("CSMSG_NOT_USER", channel->name);
6346 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6347 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6349 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6352 static MODCMD_FUNC(user_opt_autoinvite)
6354 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6356 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6358 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6361 static MODCMD_FUNC(user_opt_info)
6363 struct userData *uData;
6366 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6370 /* If they got past the command restrictions (which require access)
6371 * but fail this test, we have some fool with security override on.
6373 reply("CSMSG_NOT_USER", channel->name);
6380 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6381 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6383 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6386 bp = strcspn(infoline, "\001");
6389 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6394 if(infoline[0] == '*' && infoline[1] == 0)
6397 uData->info = strdup(infoline);
6400 reply("CSMSG_USET_INFO", uData->info);
6402 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6406 struct svccmd_list uset_shows_list;
6408 static CHANSERV_FUNC(cmd_uset)
6410 struct svccmd *subcmd;
6414 /* Check if we need to (re-)initialize uset_shows_list. */
6415 if(!uset_shows_list.used)
6419 "NoAutoOp", "AutoInvite", "Info"
6422 if(!uset_shows_list.size)
6424 uset_shows_list.size = ArrayLength(options);
6425 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6427 for(ii = 0; ii < ArrayLength(options); ii++)
6429 const char *name = options[ii];
6430 sprintf(buf, "%s %s", argv[0], name);
6431 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6434 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6437 svccmd_list_append(&uset_shows_list, subcmd);
6443 /* Do this so options are presented in a consistent order. */
6444 reply("CSMSG_USER_OPTIONS");
6445 for(ii = 0; ii < uset_shows_list.used; ii++)
6446 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6450 sprintf(buf, "%s %s", argv[0], argv[1]);
6451 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6454 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6458 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6461 static CHANSERV_FUNC(cmd_giveownership)
6463 struct handle_info *new_owner_hi;
6464 struct userData *new_owner;
6465 struct userData *curr_user;
6466 struct userData *invoker;
6467 struct chanData *cData = channel->channel_info;
6468 struct do_not_register *dnr;
6469 const char *confirm;
6470 struct giveownership *giveownership;
6471 unsigned int force, override;
6472 unsigned short co_access, new_owner_old_access;
6473 char reason[MAXLEN], transfer_reason[MAXLEN];
6476 curr_user = GetChannelAccess(cData, user->handle_info);
6477 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6479 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6480 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6481 && (uData->access > 500)
6482 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6483 || uData->access < 500));
6485 if(!curr_user || (curr_user->access != UL_OWNER))
6487 struct userData *owner = NULL;
6488 for(curr_user = channel->channel_info->users;
6490 curr_user = curr_user->next)
6492 if(curr_user->access != UL_OWNER)
6496 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6503 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6505 char delay[INTERVALLEN];
6506 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6507 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6510 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6512 if(new_owner_hi == user->handle_info)
6514 reply("CSMSG_NO_TRANSFER_SELF");
6517 new_owner = GetChannelAccess(cData, new_owner_hi);
6522 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6526 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6530 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6532 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6535 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6536 if(!IsHelping(user))
6537 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6539 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6542 invoker = GetChannelUser(cData, user->handle_info);
6543 if(invoker->access <= UL_OWNER)
6545 confirm = make_confirmation_string(curr_user);
6546 if((argc < 3) || strcmp(argv[2], confirm))
6548 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6552 new_owner_old_access = new_owner->access;
6553 if(new_owner->access >= UL_COOWNER)
6554 co_access = new_owner->access;
6556 co_access = UL_COOWNER;
6557 new_owner->access = UL_OWNER;
6559 curr_user->access = co_access;
6560 cData->ownerTransfer = now;
6561 giveownership = calloc(1, sizeof(*giveownership));
6562 giveownership->issued = now;
6563 giveownership->old_owner = curr_user->handle->handle;
6564 giveownership->target = new_owner_hi->handle;
6565 giveownership->target_access = new_owner_old_access;
6568 if(argc > (2 + force))
6570 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6571 giveownership->reason = strdup(transfer_reason);
6573 giveownership->staff_issuer = strdup(user->handle_info->handle);
6576 giveownership->previous = channel->channel_info->giveownership;
6577 channel->channel_info->giveownership = giveownership;
6578 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6579 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6580 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6584 static CHANSERV_FUNC(cmd_suspend)
6586 struct handle_info *hi;
6587 struct userData *actor, *real_actor, *target;
6588 unsigned int override = 0;
6591 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6592 actor = GetChannelUser(channel->channel_info, user->handle_info);
6593 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6594 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6596 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6599 if(target->access >= actor->access)
6601 reply("MSG_USER_OUTRANKED", hi->handle);
6604 if(target->flags & USER_SUSPENDED)
6606 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6611 target->present = 0;
6614 if(!real_actor || target->access >= real_actor->access)
6615 override = CMD_LOG_OVERRIDE;
6616 target->flags |= USER_SUSPENDED;
6617 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6618 return 1 | override;
6621 static CHANSERV_FUNC(cmd_unsuspend)
6623 struct handle_info *hi;
6624 struct userData *actor, *real_actor, *target;
6625 unsigned int override = 0;
6628 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6629 actor = GetChannelUser(channel->channel_info, user->handle_info);
6630 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6631 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6633 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6636 if(target->access >= actor->access)
6638 reply("MSG_USER_OUTRANKED", hi->handle);
6641 if(!(target->flags & USER_SUSPENDED))
6643 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6646 if(!real_actor || target->access >= real_actor->access)
6647 override = CMD_LOG_OVERRIDE;
6648 target->flags &= ~USER_SUSPENDED;
6649 scan_user_presence(target, NULL);
6650 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6651 return 1 | override;
6654 static MODCMD_FUNC(cmd_deleteme)
6656 struct handle_info *hi;
6657 struct userData *target;
6658 const char *confirm_string;
6659 unsigned short access_level;
6662 hi = user->handle_info;
6663 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6665 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6668 if(target->access == UL_OWNER)
6670 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6673 confirm_string = make_confirmation_string(target);
6674 if((argc < 2) || strcmp(argv[1], confirm_string))
6676 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6679 access_level = target->access;
6680 channel_name = strdup(channel->name);
6681 del_channel_user(target, 1);
6682 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6687 static CHANSERV_FUNC(cmd_addvote)
6689 struct chanData *cData = channel->channel_info;
6690 struct userData *uData, *target;
6691 struct handle_info *hi;
6692 if (!cData) return 0;
6694 hi = user->handle_info;
6695 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6697 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6700 if(target->access < 300) {
6701 reply("CSMSG_NO_ACCESS");
6705 reply("CSMSG_ADDVOTE_FULL");
6709 msg = unsplit_string(argv + 1, argc - 1, NULL);
6710 cData->vote = strdup(msg);
6711 cData->vote_start=0;
6712 dict_delete(cData->vote_options);
6713 cData->vote_options = dict_new();
6714 dict_set_free_data(cData->vote_options, free_vote_options);
6715 for(uData = channel->channel_info->users; uData; uData = uData->next)
6720 reply("CSMSG_ADDVOTE_DONE");
6724 static CHANSERV_FUNC(cmd_delvote)
6726 struct chanData *cData = channel->channel_info;
6727 struct userData *target;
6728 struct handle_info *hi;
6729 if (!cData) return 0;
6730 hi = user->handle_info;
6731 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6733 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6736 if(target->access < 300) {
6737 reply("CSMSG_NO_ACCESS");
6741 reply("CSMSG_NO_VOTE");
6746 reply("CSMSG_DELVOTE_DONE");
6750 static CHANSERV_FUNC(cmd_addoption)
6752 struct chanData *cData = channel->channel_info;
6753 struct userData *target;
6754 struct handle_info *hi;
6755 if (!cData) return 0;
6757 hi = user->handle_info;
6758 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6760 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6763 if(target->access < 300) {
6764 reply("CSMSG_NO_ACCESS");
6768 reply("CSMSG_NO_VOTE");
6774 msg = unsplit_string(argv + 1, argc - 1, NULL);
6777 unsigned int lastid = 1;
6778 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6779 struct vote_option *cvOpt = iter_data(it);
6780 if(cvOpt->option_id > lastid)
6781 lastid = cvOpt->option_id;
6783 struct vote_option *vOpt;
6784 vOpt = calloc(1, sizeof(*vOpt));
6785 vOpt->name = strdup(msg);
6786 vOpt->option_id = (lastid + 1);
6788 sprintf(str,"%i",(lastid + 1));
6789 vOpt->option_str = strdup(str);
6791 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6793 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6797 static CHANSERV_FUNC(cmd_deloption)
6799 struct chanData *cData = channel->channel_info;
6800 struct userData *uData, *target;
6801 struct handle_info *hi;
6802 if (!cData) return 0;
6804 hi = user->handle_info;
6805 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6807 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6810 if(target->access < 300) {
6811 reply("CSMSG_NO_ACCESS");
6815 reply("CSMSG_NO_VOTE");
6818 if(cData->vote_start) {
6819 if(dict_size(cData->vote_options) < 3) {
6820 reply("CSMSG_VOTE_NEED_OPTIONS");
6825 int find_id = atoi(argv[1]);
6827 unsigned int found = 0;
6830 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6832 if (find_id == ii) {
6833 struct vote_option *vOpt = iter_data(it);
6834 found = vOpt->option_id;
6836 sprintf(str,"%i",vOpt->option_id);
6837 dict_remove(cData->vote_options, str);
6842 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6843 if(uData->votefor == found) {
6848 reply("CSMSG_DELOPTION_DONE");
6851 reply("CSMSG_DELOPTION_NONE");
6856 static CHANSERV_FUNC(cmd_vote)
6858 struct chanData *cData = channel->channel_info;
6859 struct userData *target;
6860 struct handle_info *hi;
6861 unsigned int votedfor = 0;
6862 char *votedfor_str = NULL;
6864 if (!cData || !cData->vote) {
6865 reply("CSMSG_NO_VOTE");
6868 if(argc > 1 && cData->vote_start) {
6869 hi = user->handle_info;
6870 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6872 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6875 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6876 reply("CSMSG_NO_ACCESS");
6880 reply("CSMSG_VOTE_VOTED");
6883 int find_id = atoi(argv[1]);
6886 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6888 if (find_id == ii) {
6889 struct vote_option *vOpt = iter_data(it);
6892 target->votefor = vOpt->option_id;
6893 votedfor = vOpt->option_id;
6894 votedfor_str = vOpt->name;
6898 reply("CSMSG_VOTE_INVALID");
6902 if (!cData->vote_start) {
6903 reply("CSMSG_VOTE_NOT_STARTED");
6905 reply("CSMSG_VOTE_QUESTION",cData->vote);
6907 unsigned int voteid = 0;
6910 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6911 struct vote_option *vOpt = iter_data(it);
6913 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6915 if(argc > 1 && cData->vote_start && votedfor_str) {
6916 reply("CSMSG_VOTE_DONE",votedfor_str);
6921 static CHANSERV_FUNC(cmd_startvote)
6923 struct chanData *cData = channel->channel_info;
6924 struct userData *target;
6925 struct handle_info *hi;
6926 if (!cData) return 0;
6927 hi = user->handle_info;
6928 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6930 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6933 if(target->access < 300) {
6934 reply("CSMSG_NO_ACCESS");
6938 reply("CSMSG_NO_VOTE");
6941 if(cData->vote_start) {
6942 reply("CSMSG_STARTVOTE_RUNNING");
6945 if(dict_size(cData->vote_options) < 2) {
6946 reply("CSMSG_VOTE_NEED_OPTIONS");
6949 cData->vote_start = 1;
6950 char response[MAXLEN];
6951 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6952 irc_privmsg(cmd->parent->bot, channel->name, response);
6953 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6954 irc_privmsg(cmd->parent->bot, channel->name, response);
6955 unsigned int voteid = 0;
6957 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6958 struct vote_option *vOpt = iter_data(it);
6960 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6961 irc_privmsg(cmd->parent->bot, channel->name, response);
6963 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6964 irc_privmsg(cmd->parent->bot, channel->name, response);
6965 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6966 irc_privmsg(cmd->parent->bot, channel->name, response);
6970 static CHANSERV_FUNC(cmd_endvote)
6972 struct chanData *cData = channel->channel_info;
6973 struct userData *target;
6974 struct handle_info *hi;
6975 if (!cData) return 0;
6976 hi = user->handle_info;
6977 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6979 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6982 if(target->access < 300) {
6983 reply("CSMSG_NO_ACCESS");
6987 reply("CSMSG_NO_VOTE");
6990 if(!cData->vote_start) {
6991 reply("CSMSG_ENDVOTE_STOPPED");
6994 cData->vote_start = 0;
6995 reply("CSMSG_ENDVOTE_DONE");
6999 static CHANSERV_FUNC(cmd_voteresults)
7001 struct chanData *cData = channel->channel_info;
7002 struct userData *target;
7003 struct handle_info *hi;
7004 if (!cData) return 0;
7006 reply("CSMSG_NO_VOTE");
7009 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7010 hi = user->handle_info;
7011 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7013 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7016 if(target->access < 300) {
7017 reply("CSMSG_NO_ACCESS");
7020 char response[MAXLEN];
7021 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7022 irc_privmsg(cmd->parent->bot, channel->name, response);
7023 unsigned int voteid = 0;
7025 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7026 struct vote_option *vOpt = iter_data(it);
7028 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7029 irc_privmsg(cmd->parent->bot, channel->name, response);
7032 reply("CSMSG_VOTE_QUESTION",cData->vote);
7033 unsigned int voteid = 0;
7035 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7036 struct vote_option *vOpt = iter_data(it);
7038 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7045 chanserv_refresh_topics(UNUSED_ARG(void *data))
7047 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7048 struct chanData *cData;
7051 for(cData = channelList; cData; cData = cData->next)
7053 if(IsSuspended(cData))
7055 opt = cData->chOpts[chTopicRefresh];
7058 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7061 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7062 cData->last_refresh = refresh_num;
7064 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7067 static CHANSERV_FUNC(cmd_unf)
7071 char response[MAXLEN];
7072 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7073 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7074 irc_privmsg(cmd->parent->bot, channel->name, response);
7077 reply("CSMSG_UNF_RESPONSE");
7081 static CHANSERV_FUNC(cmd_ping)
7085 char response[MAXLEN];
7086 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7087 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7088 irc_privmsg(cmd->parent->bot, channel->name, response);
7091 reply("CSMSG_PING_RESPONSE");
7095 static CHANSERV_FUNC(cmd_wut)
7099 char response[MAXLEN];
7100 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7101 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7102 irc_privmsg(cmd->parent->bot, channel->name, response);
7105 reply("CSMSG_WUT_RESPONSE");
7109 static CHANSERV_FUNC(cmd_8ball)
7111 unsigned int i, j, accum;
7116 for(i=1; i<argc; i++)
7117 for(j=0; argv[i][j]; j++)
7118 accum = (accum << 5) - accum + toupper(argv[i][j]);
7119 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7122 char response[MAXLEN];
7123 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7124 irc_privmsg(cmd->parent->bot, channel->name, response);
7127 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7131 static CHANSERV_FUNC(cmd_d)
7133 unsigned long sides, count, modifier, ii, total;
7134 char response[MAXLEN], *sep;
7138 if((count = strtoul(argv[1], &sep, 10)) < 1)
7148 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7149 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7153 else if((sep[0] == '-') && isdigit(sep[1]))
7154 modifier = strtoul(sep, NULL, 10);
7155 else if((sep[0] == '+') && isdigit(sep[1]))
7156 modifier = strtoul(sep+1, NULL, 10);
7163 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7168 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7171 for(total = ii = 0; ii < count; ++ii)
7172 total += (rand() % sides) + 1;
7175 if((count > 1) || modifier)
7177 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7178 sprintf(response, fmt, total, count, sides, modifier);
7182 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7183 sprintf(response, fmt, total, sides);
7186 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7188 send_message_type(4, user, cmd->parent->bot, "%s", response);
7192 static CHANSERV_FUNC(cmd_huggle)
7194 /* CTCP must be via PRIVMSG, never notice */
7196 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7198 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7203 chanserv_adjust_limit(void *data)
7205 struct mod_chanmode change;
7206 struct chanData *cData = data;
7207 struct chanNode *channel = cData->channel;
7210 if(IsSuspended(cData))
7213 cData->limitAdjusted = now;
7214 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7215 if(cData->modes.modes_set & MODE_LIMIT)
7217 if(limit > cData->modes.new_limit)
7218 limit = cData->modes.new_limit;
7219 else if(limit == cData->modes.new_limit)
7223 mod_chanmode_init(&change);
7224 change.modes_set = MODE_LIMIT;
7225 change.new_limit = limit;
7226 mod_chanmode_announce(chanserv, channel, &change);
7230 handle_new_channel(struct chanNode *channel)
7232 struct chanData *cData;
7234 if(!(cData = channel->channel_info))
7237 if(cData->modes.modes_set || cData->modes.modes_clear)
7238 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7240 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7241 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7244 void handle_new_channel_created(char *chan, struct userNode *user) {
7245 if(user->handle_info && chanserv_conf.new_channel_authed) {
7246 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7247 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7248 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7250 if(chanserv_conf.new_channel_msg)
7251 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7254 /* Welcome to my worst nightmare. Warning: Read (or modify)
7255 the code below at your own risk. */
7257 handle_join(struct modeNode *mNode)
7259 struct mod_chanmode change;
7260 struct userNode *user = mNode->user;
7261 struct chanNode *channel = mNode->channel;
7262 struct chanData *cData;
7263 struct userData *uData = NULL;
7264 struct banData *bData;
7265 struct handle_info *handle;
7266 unsigned int modes = 0, info = 0;
7270 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7273 cData = channel->channel_info;
7274 if(channel->members.used > cData->max) {
7275 cData->max = channel->members.used;
7276 cData->max_time = now;
7279 for(i = 0; i < channel->invited.used; i++)
7281 if(channel->invited.list[i] == user) {
7282 userList_remove(&channel->invited, user);
7286 /* Check for bans. If they're joining through a ban, one of two
7288 * 1: Join during a netburst, by riding the break. Kick them
7289 * unless they have ops or voice in the channel.
7290 * 2: They're allowed to join through the ban (an invite in
7291 * ircu2.10, or a +e on Hybrid, or something).
7292 * If they're not joining through a ban, and the banlist is not
7293 * full, see if they're on the banlist for the channel. If so,
7296 if(user->uplink->burst && !mNode->modes)
7299 for(ii = 0; ii < channel->banlist.used; ii++)
7301 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7303 /* Riding a netburst. Naughty. */
7304 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7310 mod_chanmode_init(&change);
7312 if(channel->banlist.used < MAXBANS)
7314 /* Not joining through a ban. */
7315 for(bData = cData->bans;
7316 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7317 bData = bData->next);
7321 char kick_reason[MAXLEN];
7322 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7324 bData->triggered = now;
7325 if(bData != cData->bans)
7327 /* Shuffle the ban to the head of the list. */
7329 bData->next->prev = bData->prev;
7331 bData->prev->next = bData->next;
7334 bData->next = cData->bans;
7337 cData->bans->prev = bData;
7338 cData->bans = bData;
7341 change.args[0].mode = MODE_BAN;
7342 change.args[0].u.hostmask = bData->mask;
7343 mod_chanmode_announce(chanserv, channel, &change);
7344 KickChannelUser(user, channel, chanserv, kick_reason);
7349 /* ChanServ will not modify the limits in join-flooded channels,
7350 or when there are enough slots left below the limit. */
7351 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7352 && !channel->join_flooded
7353 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7355 /* The user count has begun "bumping" into the channel limit,
7356 so set a timer to raise the limit a bit. Any previous
7357 timers are removed so three incoming users within the delay
7358 results in one limit change, not three. */
7360 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7361 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7364 if(channel->join_flooded)
7366 /* don't automatically give ops or voice during a join flood */
7368 else if(cData->lvlOpts[lvlGiveOps] == 0)
7369 modes |= MODE_CHANOP;
7370 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7371 modes |= MODE_VOICE;
7373 greeting = cData->greeting;
7374 if(user->handle_info)
7376 handle = user->handle_info;
7378 if(IsHelper(user) && !IsHelping(user))
7381 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7383 if(channel == chanserv_conf.support_channels.list[ii])
7385 HANDLE_SET_FLAG(user->handle_info, HELPING);
7391 uData = GetTrueChannelAccess(cData, handle);
7392 if(uData && !IsUserSuspended(uData))
7394 /* Ops and above were handled by the above case. */
7395 if(IsUserAutoOp(uData))
7397 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7398 modes |= MODE_CHANOP;
7399 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7400 modes |= MODE_VOICE;
7402 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7403 cData->visited = now;
7404 if(cData->user_greeting)
7405 greeting = cData->user_greeting;
7407 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7408 && ((now - uData->seen) >= chanserv_conf.info_delay)
7416 /* If user joining normally (not during burst), apply op or voice,
7417 * and send greeting/userinfo as appropriate.
7419 if(!user->uplink->burst)
7423 if(modes & MODE_CHANOP)
7424 modes &= ~MODE_VOICE;
7425 change.args[0].mode = modes;
7426 change.args[0].u.member = mNode;
7427 mod_chanmode_announce(chanserv, channel, &change);
7430 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7431 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7432 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7438 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7440 struct mod_chanmode change;
7441 struct userData *channel;
7442 unsigned int ii, jj;
7444 if(!user->handle_info)
7447 mod_chanmode_init(&change);
7449 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7451 struct chanNode *cn;
7452 struct modeNode *mn;
7453 if(IsUserSuspended(channel)
7454 || IsSuspended(channel->channel)
7455 || !(cn = channel->channel->channel))
7458 mn = GetUserMode(cn, user);
7461 if(!IsUserSuspended(channel)
7462 && IsUserAutoInvite(channel)
7463 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7465 && !user->uplink->burst)
7466 irc_invite(chanserv, user, cn);
7470 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7471 channel->channel->visited = now;
7473 if(IsUserAutoOp(channel))
7475 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7476 change.args[0].mode = MODE_CHANOP;
7477 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7478 change.args[0].mode = MODE_VOICE;
7480 change.args[0].mode = 0;
7481 change.args[0].u.member = mn;
7482 if(change.args[0].mode)
7483 mod_chanmode_announce(chanserv, cn, &change);
7486 channel->seen = now;
7487 channel->present = 1;
7490 for(ii = 0; ii < user->channels.used; ++ii)
7492 struct chanNode *chan = user->channels.list[ii]->channel;
7493 struct banData *ban;
7495 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7496 || !chan->channel_info
7497 || IsSuspended(chan->channel_info))
7499 for(jj = 0; jj < chan->banlist.used; ++jj)
7500 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7502 if(jj < chan->banlist.used)
7504 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7506 char kick_reason[MAXLEN];
7507 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7509 change.args[0].mode = MODE_BAN;
7510 change.args[0].u.hostmask = ban->mask;
7511 mod_chanmode_announce(chanserv, chan, &change);
7512 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7513 KickChannelUser(user, chan, chanserv, kick_reason);
7514 ban->triggered = now;
7519 if(IsSupportHelper(user))
7521 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7523 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7525 HANDLE_SET_FLAG(user->handle_info, HELPING);
7533 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7535 struct chanData *cData;
7536 struct userData *uData;
7538 cData = mn->channel->channel_info;
7539 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7542 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7544 /* Allow for a bit of padding so that the limit doesn't
7545 track the user count exactly, which could get annoying. */
7546 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7548 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7549 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7553 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7555 scan_user_presence(uData, mn->user);
7557 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7558 cData->visited = now;
7561 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7564 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7565 struct chanNode *channel;
7566 struct userNode *exclude;
7567 /* When looking at the channel that is being /part'ed, we
7568 * have to skip over the client that is leaving. For
7569 * other channels, we must not do that.
7571 channel = chanserv_conf.support_channels.list[ii];
7572 exclude = (channel == mn->channel) ? mn->user : NULL;
7573 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7576 if(ii == chanserv_conf.support_channels.used)
7577 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7582 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7584 struct userData *uData;
7586 if(!channel->channel_info || !kicker || IsService(kicker)
7587 || (kicker == victim) || IsSuspended(channel->channel_info)
7588 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7591 if(protect_user(victim, kicker, channel->channel_info))
7593 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7594 KickChannelUser(kicker, channel, chanserv, reason);
7597 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7602 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7604 struct chanData *cData;
7606 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7609 cData = channel->channel_info;
7610 if(bad_topic(channel, user, channel->topic))
7612 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7613 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7614 SetChannelTopic(channel, chanserv, old_topic, 1);
7615 else if(cData->topic)
7616 SetChannelTopic(channel, chanserv, cData->topic, 1);
7619 /* With topicsnarf, grab the topic and save it as the default topic. */
7620 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7623 cData->topic = strdup(channel->topic);
7629 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7631 struct mod_chanmode *bounce = NULL;
7632 unsigned int bnc, ii;
7635 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7638 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7639 && mode_lock_violated(&channel->channel_info->modes, change))
7641 char correct[MAXLEN];
7642 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7643 mod_chanmode_format(&channel->channel_info->modes, correct);
7644 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7646 for(ii = bnc = 0; ii < change->argc; ++ii)
7648 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7650 const struct userNode *victim = change->args[ii].u.member->user;
7651 if(!protect_user(victim, user, channel->channel_info))
7654 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7657 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7658 bounce->args[bnc].u.member = GetUserMode(channel, user);
7659 if(bounce->args[bnc].u.member)
7663 bounce->args[bnc].mode = MODE_CHANOP;
7664 bounce->args[bnc].u.member = change->args[ii].u.member;
7666 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7668 else if(change->args[ii].mode & MODE_CHANOP)
7670 const struct userNode *victim = change->args[ii].u.member->user;
7671 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7674 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7675 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7676 bounce->args[bnc].u.member = change->args[ii].u.member;
7679 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7681 const char *ban = change->args[ii].u.hostmask;
7682 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7685 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7686 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7687 bounce->args[bnc].u.hostmask = strdup(ban);
7689 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7694 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7695 mod_chanmode_announce(chanserv, channel, bounce);
7696 for(ii = 0; ii < change->argc; ++ii)
7697 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7698 free((char*)bounce->args[ii].u.hostmask);
7699 mod_chanmode_free(bounce);
7704 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7706 struct chanNode *channel;
7707 struct banData *bData;
7708 struct mod_chanmode change;
7709 unsigned int ii, jj;
7710 char kick_reason[MAXLEN];
7712 mod_chanmode_init(&change);
7714 change.args[0].mode = MODE_BAN;
7715 for(ii = 0; ii < user->channels.used; ++ii)
7717 channel = user->channels.list[ii]->channel;
7718 /* Need not check for bans if they're opped or voiced. */
7719 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7721 /* Need not check for bans unless channel registration is active. */
7722 if(!channel->channel_info || IsSuspended(channel->channel_info))
7724 /* Look for a matching ban already on the channel. */
7725 for(jj = 0; jj < channel->banlist.used; ++jj)
7726 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7728 /* Need not act if we found one. */
7729 if(jj < channel->banlist.used)
7731 /* Look for a matching ban in this channel. */
7732 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7734 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7736 change.args[0].u.hostmask = bData->mask;
7737 mod_chanmode_announce(chanserv, channel, &change);
7738 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7739 KickChannelUser(user, channel, chanserv, kick_reason);
7740 bData->triggered = now;
7741 break; /* we don't need to check any more bans in the channel */
7746 static void handle_rename(struct handle_info *handle, const char *old_handle)
7748 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7752 dict_remove2(handle_dnrs, old_handle, 1);
7753 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7754 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7759 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7761 struct userNode *h_user;
7763 if(handle->channels)
7765 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7766 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7768 while(handle->channels)
7769 del_channel_user(handle->channels, 1);
7774 handle_server_link(UNUSED_ARG(struct server *server))
7776 struct chanData *cData;
7778 for(cData = channelList; cData; cData = cData->next)
7780 if(!IsSuspended(cData))
7781 cData->may_opchan = 1;
7782 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7783 && !cData->channel->join_flooded
7784 && ((cData->channel->limit - cData->channel->members.used)
7785 < chanserv_conf.adjust_threshold))
7787 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7788 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7794 chanserv_conf_read(void)
7798 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7799 struct mod_chanmode *change;
7800 struct string_list *strlist;
7801 struct chanNode *chan;
7804 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7806 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7809 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7810 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7811 chanserv_conf.support_channels.used = 0;
7812 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7814 for(ii = 0; ii < strlist->used; ++ii)
7816 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7819 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7821 channelList_append(&chanserv_conf.support_channels, chan);
7824 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7827 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7830 chan = AddChannel(str, now, str2, NULL);
7832 channelList_append(&chanserv_conf.support_channels, chan);
7834 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7835 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7836 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7837 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7838 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7839 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7840 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7841 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7842 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7843 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7844 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7845 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7846 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7847 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7848 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7849 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7850 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7851 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7852 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7853 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7854 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7855 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7856 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7857 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7858 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7859 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7860 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7861 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7862 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7864 NickChange(chanserv, str, 0);
7865 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7866 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7867 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7868 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7869 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7870 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7871 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7872 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7873 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7874 chanserv_conf.max_owned = str ? atoi(str) : 5;
7875 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7876 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7877 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7878 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7879 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7880 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7881 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7882 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7883 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7884 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7885 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7886 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7887 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7890 safestrncpy(mode_line, str, sizeof(mode_line));
7891 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7892 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7893 && (change->argc < 2))
7895 chanserv_conf.default_modes = *change;
7896 mod_chanmode_free(change);
7898 free_string_list(chanserv_conf.set_shows);
7899 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7901 strlist = string_list_copy(strlist);
7904 static const char *list[] = {
7905 /* free form text */
7906 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7907 /* options based on user level */
7908 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7909 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7910 /* multiple choice options */
7911 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7912 /* binary options */
7913 "DynLimit", "NoDelete", "expire", "Vote",
7917 strlist = alloc_string_list(ArrayLength(list)-1);
7918 for(ii=0; list[ii]; ii++)
7919 string_list_append(strlist, strdup(list[ii]));
7921 chanserv_conf.set_shows = strlist;
7922 /* We don't look things up now, in case the list refers to options
7923 * defined by modules initialized after this point. Just mark the
7924 * function list as invalid, so it will be initialized.
7926 set_shows_list.used = 0;
7927 free_string_list(chanserv_conf.eightball);
7928 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7931 strlist = string_list_copy(strlist);
7935 strlist = alloc_string_list(4);
7936 string_list_append(strlist, strdup("Yes."));
7937 string_list_append(strlist, strdup("No."));
7938 string_list_append(strlist, strdup("Maybe so."));
7940 chanserv_conf.eightball = strlist;
7941 free_string_list(chanserv_conf.old_ban_names);
7942 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7944 strlist = string_list_copy(strlist);
7946 strlist = alloc_string_list(2);
7947 chanserv_conf.old_ban_names = strlist;
7948 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7949 off_channel = str ? atoi(str) : 0;
7953 chanserv_note_type_read(const char *key, struct record_data *rd)
7956 struct note_type *ntype;
7959 if(!(obj = GET_RECORD_OBJECT(rd)))
7961 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7964 if(!(ntype = chanserv_create_note_type(key)))
7966 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7970 /* Figure out set access */
7971 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7973 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7974 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7976 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7978 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7979 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7981 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7983 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7987 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7988 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7989 ntype->set_access.min_opserv = 0;
7992 /* Figure out visibility */
7993 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7994 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7995 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7996 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7997 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7998 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7999 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8000 ntype->visible_type = NOTE_VIS_ALL;
8002 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8004 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8005 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8009 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8011 struct vote_option *vOpt;
8014 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8016 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8020 vOpt = calloc(1, sizeof(*vOpt));
8021 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8022 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8023 vOpt->voted = str ? atoi(str) : 0;
8024 vOpt->option_id = str ? atoi(key) : 0;
8025 vOpt->option_str = strdup(key);
8026 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8030 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8032 struct handle_info *handle;
8033 struct userData *uData;
8034 char *seen, *inf, *flags, *voted, *votefor;
8035 unsigned long last_seen;
8036 unsigned short access_level;
8038 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8040 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8044 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8045 if(access_level > UL_OWNER)
8047 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8051 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8052 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8053 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8054 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8055 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8056 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8057 handle = get_handle_info(key);
8060 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8064 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8065 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8067 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8068 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8076 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8078 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8079 unsigned long set_time, triggered_time, expires_time;
8081 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8083 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8087 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8088 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8089 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8090 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8091 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8092 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8093 if (!reason || !owner)
8096 set_time = set ? strtoul(set, NULL, 0) : now;
8097 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8099 expires_time = strtoul(s_expires, NULL, 0);
8101 expires_time = set_time + atoi(s_duration);
8105 if(!reason || (expires_time && (expires_time < now)))
8108 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8111 static struct suspended *
8112 chanserv_read_suspended(dict_t obj)
8114 struct suspended *suspended = calloc(1, sizeof(*suspended));
8118 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8119 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8120 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8121 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8122 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8123 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8124 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8125 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8126 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8127 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8131 static struct giveownership *
8132 chanserv_read_giveownership(dict_t obj)
8134 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8138 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8139 giveownership->staff_issuer = str ? strdup(str) : NULL;
8141 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8143 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8144 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8146 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8147 giveownership->reason = str ? strdup(str) : NULL;
8148 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8149 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8151 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8152 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8153 return giveownership;
8157 chanserv_channel_read(const char *key, struct record_data *hir)
8159 struct suspended *suspended;
8160 struct giveownership *giveownership;
8161 struct mod_chanmode *modes;
8162 struct chanNode *cNode;
8163 struct chanData *cData;
8164 struct dict *channel, *obj;
8165 char *str, *argv[10];
8169 channel = hir->d.object;
8171 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8174 cNode = AddChannel(key, now, NULL, NULL);
8177 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8180 cData = register_channel(cNode, str);
8183 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8187 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8189 enum levelOption lvlOpt;
8190 enum charOption chOpt;
8192 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8193 cData->flags = atoi(str);
8195 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8197 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8199 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8200 else if(levelOptions[lvlOpt].old_flag)
8202 if(cData->flags & levelOptions[lvlOpt].old_flag)
8203 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8205 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8209 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8211 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8213 cData->chOpts[chOpt] = str[0];
8216 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8218 enum levelOption lvlOpt;
8219 enum charOption chOpt;
8222 cData->flags = base64toint(str, 5);
8223 count = strlen(str += 5);
8224 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8227 if(levelOptions[lvlOpt].old_flag)
8229 if(cData->flags & levelOptions[lvlOpt].old_flag)
8230 lvl = levelOptions[lvlOpt].flag_value;
8232 lvl = levelOptions[lvlOpt].default_value;
8234 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8236 case 'c': lvl = UL_COOWNER; break;
8237 case 'm': lvl = UL_MASTER; break;
8238 case 'n': lvl = UL_OWNER+1; break;
8239 case 'o': lvl = UL_OP; break;
8240 case 'p': lvl = UL_PEON; break;
8241 case 'w': lvl = UL_OWNER; break;
8242 default: lvl = 0; break;
8244 cData->lvlOpts[lvlOpt] = lvl;
8246 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8247 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8250 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8252 cData->expiry = atoi(str);
8253 if(cData->expiry > 0) {
8254 if(cData->expiry > now) {
8255 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8257 timeq_add(1, chanserv_expire_channel, cData);
8264 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8266 suspended = chanserv_read_suspended(obj);
8267 cData->suspended = suspended;
8268 suspended->cData = cData;
8269 /* We could use suspended->expires and suspended->revoked to
8270 * set the CHANNEL_SUSPENDED flag, but we don't. */
8272 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8274 suspended = calloc(1, sizeof(*suspended));
8275 suspended->issued = 0;
8276 suspended->revoked = 0;
8277 suspended->suspender = strdup(str);
8278 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8279 suspended->expires = str ? atoi(str) : 0;
8280 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8281 suspended->reason = strdup(str ? str : "No reason");
8282 suspended->previous = NULL;
8283 cData->suspended = suspended;
8284 suspended->cData = cData;
8288 cData->flags &= ~CHANNEL_SUSPENDED;
8289 suspended = NULL; /* to squelch a warning */
8292 if(IsSuspended(cData)) {
8293 if(suspended->expires > now)
8294 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8295 else if(suspended->expires)
8296 cData->flags &= ~CHANNEL_SUSPENDED;
8299 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8301 giveownership = chanserv_read_giveownership(obj);
8302 cData->giveownership = giveownership;
8305 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8306 struct mod_chanmode change;
8307 mod_chanmode_init(&change);
8309 change.args[0].mode = MODE_CHANOP;
8310 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8311 mod_chanmode_announce(chanserv, cNode, &change);
8314 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8315 cData->registered = str ? strtoul(str, NULL, 0) : now;
8316 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8317 cData->visited = str ? strtoul(str, NULL, 0) : now;
8318 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8319 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8320 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8321 cData->max = str ? atoi(str) : 0;
8322 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8323 cData->max_time = str ? atoi(str) : 0;
8324 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8325 cData->greeting = str ? strdup(str) : NULL;
8326 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8327 cData->user_greeting = str ? strdup(str) : NULL;
8328 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8329 cData->topic_mask = str ? strdup(str) : NULL;
8330 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8331 cData->topic = str ? strdup(str) : NULL;
8333 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8335 cData->vote = str ? strdup(str) : NULL;
8336 dict_delete(cData->vote_options);
8337 cData->vote_options = dict_new();
8338 dict_set_free_data(cData->vote_options, free_vote_options);
8339 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8340 cData->vote_start = str ? atoi(str) : 0;
8341 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8342 for(it = dict_first(obj); it; it = iter_next(it)) {
8343 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8347 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8348 for(it = dict_first(obj); it; it = iter_next(it))
8350 struct record_data *rd = iter_data(it);
8351 if(rd->type != RECDB_QSTRING) continue;
8352 int advtopic_index = atoi(iter_key(it));
8353 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8354 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8357 if(!IsSuspended(cData)
8358 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8359 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8360 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8361 cData->modes = *modes;
8363 cData->modes.modes_set |= MODE_REGISTERED;
8364 if(cData->modes.argc > 1)
8365 cData->modes.argc = 1;
8366 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8367 mod_chanmode_free(modes);
8370 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8371 for(it = dict_first(obj); it; it = iter_next(it))
8372 user_read_helper(iter_key(it), iter_data(it), cData);
8374 if(!cData->users && !IsProtected(cData))
8376 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8377 unregister_channel(cData, "has empty user list.");
8381 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8382 for(it = dict_first(obj); it; it = iter_next(it))
8383 ban_read_helper(iter_key(it), iter_data(it), cData);
8385 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8386 for(it = dict_first(obj); it; it = iter_next(it))
8388 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8389 struct record_data *rd = iter_data(it);
8390 const char *note, *setter;
8392 if(rd->type != RECDB_OBJECT)
8394 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8398 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8400 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8402 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8406 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8407 if(!setter) setter = "<unknown>";
8408 chanserv_add_channel_note(cData, ntype, setter, note);
8416 chanserv_dnr_read(const char *key, struct record_data *hir)
8418 const char *setter, *reason, *str;
8419 struct do_not_register *dnr;
8420 unsigned long expiry;
8422 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8425 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8428 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8431 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8434 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8435 expiry = str ? strtoul(str, NULL, 0) : 0;
8436 if(expiry && expiry <= now)
8438 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8441 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8443 dnr->set = atoi(str);
8449 chanserv_saxdb_read(struct dict *database)
8451 struct dict *section;
8454 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8455 for(it = dict_first(section); it; it = iter_next(it))
8456 chanserv_note_type_read(iter_key(it), iter_data(it));
8458 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8459 for(it = dict_first(section); it; it = iter_next(it))
8460 chanserv_channel_read(iter_key(it), iter_data(it));
8462 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8463 for(it = dict_first(section); it; it = iter_next(it))
8464 chanserv_dnr_read(iter_key(it), iter_data(it));
8470 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8472 int high_present = 0;
8473 saxdb_start_record(ctx, KEY_USERS, 1);
8474 for(; uData; uData = uData->next)
8476 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8478 saxdb_start_record(ctx, uData->handle->handle, 0);
8479 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8480 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8482 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8483 if(uData->channel->vote && uData->voted)
8484 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8485 if(uData->channel->vote && uData->votefor)
8486 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8488 saxdb_write_string(ctx, KEY_INFO, uData->info);
8489 saxdb_end_record(ctx);
8491 saxdb_end_record(ctx);
8492 return high_present;
8496 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8500 saxdb_start_record(ctx, KEY_BANS, 1);
8501 for(; bData; bData = bData->next)
8503 saxdb_start_record(ctx, bData->mask, 0);
8504 saxdb_write_int(ctx, KEY_SET, bData->set);
8505 if(bData->triggered)
8506 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8508 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8510 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8512 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8513 saxdb_end_record(ctx);
8515 saxdb_end_record(ctx);
8519 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8521 saxdb_start_record(ctx, name, 0);
8522 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8523 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8525 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8527 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8529 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8531 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8532 saxdb_end_record(ctx);
8536 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8538 saxdb_start_record(ctx, name, 0);
8539 if(giveownership->staff_issuer)
8540 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8541 if(giveownership->old_owner)
8542 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8543 if(giveownership->target)
8544 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8545 if(giveownership->target_access)
8546 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8547 if(giveownership->reason)
8548 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8549 if(giveownership->issued)
8550 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8551 if(giveownership->previous)
8552 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8553 saxdb_end_record(ctx);
8557 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8561 enum levelOption lvlOpt;
8562 enum charOption chOpt;
8565 saxdb_start_record(ctx, channel->channel->name, 1);
8567 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8568 saxdb_write_int(ctx, KEY_MAX, channel->max);
8569 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8571 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8572 if(channel->registrar)
8573 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8574 if(channel->greeting)
8575 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8576 if(channel->user_greeting)
8577 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8578 if(channel->topic_mask)
8579 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8580 if(channel->suspended)
8581 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8582 if(channel->giveownership)
8583 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8585 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8588 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8589 if(channel->vote_start)
8590 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8591 if (dict_size(channel->vote_options)) {
8592 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8593 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8594 struct vote_option *vOpt = iter_data(it);
8596 sprintf(str,"%i",vOpt->option_id);
8597 saxdb_start_record(ctx, str, 0);
8599 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8601 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8602 saxdb_end_record(ctx);
8604 saxdb_end_record(ctx);
8608 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8609 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8610 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8611 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8612 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8614 buf[0] = channel->chOpts[chOpt];
8616 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8618 saxdb_end_record(ctx);
8620 if(channel->modes.modes_set || channel->modes.modes_clear)
8622 mod_chanmode_format(&channel->modes, buf);
8623 saxdb_write_string(ctx, KEY_MODES, buf);
8626 high_present = chanserv_write_users(ctx, channel->users);
8627 chanserv_write_bans(ctx, channel->bans);
8629 if(channel->flags & CHANNEL_ADVTOPIC) {
8630 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8632 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8633 if(channel->advtopic[advtopic_index])
8634 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8636 saxdb_end_record(ctx);
8639 if(dict_size(channel->notes))
8643 saxdb_start_record(ctx, KEY_NOTES, 1);
8644 for(it = dict_first(channel->notes); it; it = iter_next(it))
8646 struct note *note = iter_data(it);
8647 saxdb_start_record(ctx, iter_key(it), 0);
8648 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8649 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8650 saxdb_end_record(ctx);
8652 saxdb_end_record(ctx);
8655 if(channel->ownerTransfer)
8656 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8657 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8658 saxdb_end_record(ctx);
8662 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8666 saxdb_start_record(ctx, ntype->name, 0);
8667 switch(ntype->set_access_type)
8669 case NOTE_SET_CHANNEL_ACCESS:
8670 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8672 case NOTE_SET_CHANNEL_SETTER:
8673 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8675 case NOTE_SET_PRIVILEGED: default:
8676 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8679 switch(ntype->visible_type)
8681 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8682 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8683 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8685 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8686 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8687 saxdb_end_record(ctx);
8691 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8693 struct do_not_register *dnr;
8694 dict_iterator_t it, next;
8696 for(it = dict_first(dnrs); it; it = next)
8698 next = iter_next(it);
8699 dnr = iter_data(it);
8700 if(dnr->expires && dnr->expires <= now)
8702 dict_remove(dnrs, iter_key(it));
8705 saxdb_start_record(ctx, dnr->chan_name, 0);
8707 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8709 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8710 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8711 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8712 saxdb_end_record(ctx);
8717 chanserv_saxdb_write(struct saxdb_context *ctx)
8720 struct chanData *channel;
8723 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8724 for(it = dict_first(note_types); it; it = iter_next(it))
8725 chanserv_write_note_type(ctx, iter_data(it));
8726 saxdb_end_record(ctx);
8729 saxdb_start_record(ctx, KEY_DNR, 1);
8730 write_dnrs_helper(ctx, handle_dnrs);
8731 write_dnrs_helper(ctx, plain_dnrs);
8732 write_dnrs_helper(ctx, mask_dnrs);
8733 saxdb_end_record(ctx);
8736 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8737 for(channel = channelList; channel; channel = channel->next)
8738 chanserv_write_channel(ctx, channel);
8739 saxdb_end_record(ctx);
8745 chanserv_db_cleanup(void) {
8747 unreg_part_func(handle_part);
8749 unregister_channel(channelList, "terminating.");
8750 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8751 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8752 free(chanserv_conf.support_channels.list);
8753 dict_delete(handle_dnrs);
8754 dict_delete(plain_dnrs);
8755 dict_delete(mask_dnrs);
8756 dict_delete(note_types);
8757 free_string_list(chanserv_conf.eightball);
8758 free_string_list(chanserv_conf.old_ban_names);
8759 free_string_list(chanserv_conf.set_shows);
8760 free(set_shows_list.list);
8761 free(uset_shows_list.list);
8764 struct userData *helper = helperList;
8765 helperList = helperList->next;
8770 #if defined(GCC_VARMACROS)
8771 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8772 #elif defined(C99_VARMACROS)
8773 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8775 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8776 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8779 init_chanserv(const char *nick)
8781 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8782 conf_register_reload(chanserv_conf_read);
8786 reg_server_link_func(handle_server_link);
8787 reg_new_channel_func(handle_new_channel);
8788 reg_join_func(handle_join);
8789 reg_part_func(handle_part);
8790 reg_kick_func(handle_kick);
8791 reg_topic_func(handle_topic);
8792 reg_mode_change_func(handle_mode);
8793 reg_nick_change_func(handle_nick_change);
8794 reg_auth_func(handle_auth);
8797 reg_handle_rename_func(handle_rename);
8798 reg_unreg_func(handle_unreg);
8800 handle_dnrs = dict_new();
8801 dict_set_free_data(handle_dnrs, free);
8802 plain_dnrs = dict_new();
8803 dict_set_free_data(plain_dnrs, free);
8804 mask_dnrs = dict_new();
8805 dict_set_free_data(mask_dnrs, free);
8807 reg_svccmd_unbind_func(handle_svccmd_unbind);
8808 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8809 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8810 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8811 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8812 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8813 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8814 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8815 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8816 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8817 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8818 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8819 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8820 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8822 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8823 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8825 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8826 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8827 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8828 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8829 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8831 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8832 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8833 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8834 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8835 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8837 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8838 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8839 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8840 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8842 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8843 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8844 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8845 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8846 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8847 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8848 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8849 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8851 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8852 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8853 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8854 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8855 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8856 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8857 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8858 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8859 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8860 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8861 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8862 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8863 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8864 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8865 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8867 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8868 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8869 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8870 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8871 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8873 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8874 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8876 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8877 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8878 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8879 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8880 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8881 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8882 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8883 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8884 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8885 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8886 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8888 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8889 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8891 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8892 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8893 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8894 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8896 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8897 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8898 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8899 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8900 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8902 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8903 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8904 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8905 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8906 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8907 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8909 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8910 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8911 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8912 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8913 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8914 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8915 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8916 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8918 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8920 /* Channel options */
8921 DEFINE_CHANNEL_OPTION(defaulttopic);
8922 DEFINE_CHANNEL_OPTION(topicmask);
8923 DEFINE_CHANNEL_OPTION(greeting);
8924 DEFINE_CHANNEL_OPTION(usergreeting);
8925 DEFINE_CHANNEL_OPTION(modes);
8926 DEFINE_CHANNEL_OPTION(enfops);
8927 DEFINE_CHANNEL_OPTION(giveops);
8928 DEFINE_CHANNEL_OPTION(protect);
8929 DEFINE_CHANNEL_OPTION(enfmodes);
8930 DEFINE_CHANNEL_OPTION(enftopic);
8931 DEFINE_CHANNEL_OPTION(pubcmd);
8932 DEFINE_CHANNEL_OPTION(givevoice);
8933 DEFINE_CHANNEL_OPTION(userinfo);
8934 DEFINE_CHANNEL_OPTION(dynlimit);
8935 DEFINE_CHANNEL_OPTION(topicsnarf);
8936 DEFINE_CHANNEL_OPTION(vote);
8937 DEFINE_CHANNEL_OPTION(nodelete);
8938 DEFINE_CHANNEL_OPTION(toys);
8939 DEFINE_CHANNEL_OPTION(setters);
8940 DEFINE_CHANNEL_OPTION(topicrefresh);
8941 DEFINE_CHANNEL_OPTION(ctcpusers);
8942 DEFINE_CHANNEL_OPTION(ctcpreaction);
8943 DEFINE_CHANNEL_OPTION(inviteme);
8944 DEFINE_CHANNEL_OPTION(advtopic);
8945 DEFINE_CHANNEL_OPTION(unreviewed);
8946 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8947 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8948 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8950 DEFINE_CHANNEL_OPTION(offchannel);
8951 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8953 /* Alias set topic to set defaulttopic for compatibility. */
8954 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8957 DEFINE_USER_OPTION(noautoop);
8958 DEFINE_USER_OPTION(autoinvite);
8959 DEFINE_USER_OPTION(info);
8961 /* Alias uset autovoice to uset autoop. */
8962 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8964 note_types = dict_new();
8965 dict_set_free_data(note_types, chanserv_deref_note_type);
8968 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8969 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8970 service_register(chanserv)->trigger = '!';
8971 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8973 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8975 if(chanserv_conf.channel_expire_frequency)
8976 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8978 if(chanserv_conf.dnr_expire_frequency)
8979 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8981 if(chanserv_conf.refresh_period)
8983 unsigned long next_refresh;
8984 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8985 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8988 reg_exit_func(chanserv_db_cleanup);
8989 message_register_table(msgtab);