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_MIN_TIME_BANS "min_time_bans"
47 #define KEY_NICK "nick"
48 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
49 #define KEY_8BALL_RESPONSES "8ball"
50 #define KEY_OLD_BAN_NAMES "old_ban_names"
51 #define KEY_REFRESH_PERIOD "refresh_period"
52 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
53 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
54 #define KEY_MAX_OWNED "max_owned"
55 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
56 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
57 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
58 #define KEY_NODELETE_LEVEL "nodelete_level"
59 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
60 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
61 #define KEY_INVITED_INTERVAL "invite_timeout"
62 #define KEY_REVOKE_MODE_A "revoke_mode_a"
63 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
64 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
65 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
67 /* ChanServ database */
68 #define KEY_CHANNELS "channels"
69 #define KEY_NOTE_TYPES "note_types"
71 /* Note type parameters */
72 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
73 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
74 #define KEY_NOTE_SETTER_ACCESS "setter_access"
75 #define KEY_NOTE_VISIBILITY "visibility"
76 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
77 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
78 #define KEY_NOTE_VIS_ALL "all"
79 #define KEY_NOTE_MAX_LENGTH "max_length"
80 #define KEY_NOTE_SETTER "setter"
81 #define KEY_NOTE_NOTE "note"
83 /* Do-not-register channels */
85 #define KEY_DNR_SET "set"
86 #define KEY_DNR_SETTER "setter"
87 #define KEY_DNR_REASON "reason"
90 #define KEY_REGISTERED "registered"
91 #define KEY_REGISTRAR "registrar"
92 #define KEY_SUSPENDED "suspended"
93 #define KEY_PREVIOUS "previous"
94 #define KEY_SUSPENDER "suspender"
95 #define KEY_ISSUED "issued"
96 #define KEY_REVOKED "revoked"
97 #define KEY_SUSPEND_EXPIRES "suspend_expires"
98 #define KEY_SUSPEND_REASON "suspend_reason"
99 #define KEY_GIVEOWNERSHIP "giveownership"
100 #define KEY_STAFF_ISSUER "staff_issuer"
101 #define KEY_OLD_OWNER "old_owner"
102 #define KEY_TARGET "target"
103 #define KEY_TARGET_ACCESS "target_access"
104 #define KEY_VISITED "visited"
105 #define KEY_TOPIC "topic"
106 #define KEY_GREETING "greeting"
107 #define KEY_USER_GREETING "user_greeting"
108 #define KEY_MODES "modes"
109 #define KEY_FLAGS "flags"
110 #define KEY_OPTIONS "options"
111 #define KEY_USERS "users"
112 #define KEY_BANS "bans"
113 #define KEY_MAX "max"
114 #define KEY_MAX_TIME "max_time"
115 #define KEY_NOTES "notes"
116 #define KEY_TOPIC_MASK "topic_mask"
117 #define KEY_ADVTOPIC_ENTRIES "adv_topic"
118 #define KEY_OWNER_TRANSFER "owner_transfer"
119 #define KEY_EXPIRE "expire"
122 #define KEY_LEVEL "level"
123 #define KEY_INFO "info"
124 #define KEY_SEEN "seen"
127 #define KEY_VOTE "vote"
128 #define KEY_VOTE_START "votestart"
129 #define KEY_VOTE_OPTIONS "voptions"
130 #define KEY_VOTE_OPTION_NAME "voptionname"
131 #define KEY_VOTE_VOTED "vvoted"
132 #define KEY_VOTE_VOTEDFOR "vvotefor"
133 #define KEY_VOTE_OPTION_ID "voptionid"
134 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
137 #define KEY_OWNER "owner"
138 #define KEY_REASON "reason"
139 #define KEY_SET "set"
140 #define KEY_DURATION "duration"
141 #define KEY_EXPIRES "expires"
142 #define KEY_TRIGGERED "triggered"
144 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_UNREVIEWED)
145 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
146 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
148 /* Administrative messages */
149 static const struct message_entry msgtab[] = {
150 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
152 /* Channel registration */
153 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
154 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
155 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
156 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
157 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
158 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
160 /* Do-not-register channels */
161 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
162 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
163 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
164 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
165 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
166 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
167 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
168 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
169 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
170 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
171 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
172 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
173 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
174 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
176 /* Channel unregistration */
177 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
178 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
179 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
180 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
183 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
184 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
186 /* Channel merging */
187 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
188 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
189 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
190 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
191 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
193 /* Handle unregistration */
194 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
197 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
198 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
199 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
200 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
201 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
202 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
203 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
204 { "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." },
205 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
206 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
207 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
208 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
209 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
210 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
212 /* Removing yourself from a channel. */
213 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
214 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
215 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
217 /* User management */
218 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
219 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
220 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
221 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
222 { "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." },
223 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
224 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
225 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
227 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
228 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
229 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
230 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
231 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
232 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
233 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
236 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
237 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
238 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
239 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
240 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
241 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
242 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
243 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
244 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
245 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
246 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
247 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
248 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
249 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
250 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
251 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
253 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
255 /* Channel management */
256 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
257 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
258 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
260 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
261 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
262 { "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" },
263 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
264 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
265 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
266 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
267 { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
269 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
270 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
271 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
272 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
273 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
274 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
275 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
276 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
277 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
278 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
279 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
280 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
281 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
282 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
283 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
284 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
285 { "CSMSG_SET_ADVTOPIC", "$bAdvTopic $b %s" },
286 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
287 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
288 { "CSMSG_SET_MODES", "$bModes $b %s" },
289 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
290 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
291 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
292 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
293 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
294 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
295 { "CSMSG_SET_VOTE", "$bVote $b %d" },
296 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
297 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
298 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
299 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
300 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
301 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
302 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
303 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
304 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
305 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
306 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
307 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
308 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
309 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
310 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
311 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
312 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
313 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
314 { "CSMSG_USET_INFO", "$bInfo $b %s" },
316 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
317 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
318 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
319 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
320 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
321 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
322 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
323 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
324 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
325 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
326 { "CSMSG_PROTECT_NONE", "No users will be protected." },
327 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
328 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
329 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
330 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
331 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
332 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
333 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
334 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
335 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
336 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
337 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
338 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
340 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
341 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
342 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
343 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
344 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
345 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
346 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
347 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
349 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
350 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
351 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
353 /* Channel userlist */
354 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
355 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
356 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
357 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
358 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
360 /* Channel note list */
361 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
362 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
363 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
364 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
365 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
366 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
367 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
368 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
369 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
370 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
371 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
372 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
373 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
374 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
375 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
377 /* Channel [un]suspension */
378 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
379 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
380 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
381 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
382 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
383 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
384 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
386 /* Access information */
387 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
388 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
389 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
390 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
391 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
392 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
393 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
394 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
395 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
396 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
397 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
398 { "CSMSG_UC_H_TITLE", "network helper" },
399 { "CSMSG_LC_H_TITLE", "support helper" },
400 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
401 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
402 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
405 /* Seen information */
406 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
407 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
408 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
409 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
411 /* Names information */
412 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
413 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
415 /* Channel information */
416 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
417 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
418 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
419 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
420 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
421 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
422 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
423 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
424 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
425 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
426 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
427 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
428 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
435 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
436 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
437 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
439 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
440 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
441 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
442 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
444 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
445 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
446 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
447 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
448 { "CSMSG_PEEK_OPS", "$bOps:$b" },
449 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
451 /* Network information */
452 { "CSMSG_NETWORK_INFO", "Network Information:" },
453 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
454 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
455 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
456 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
457 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
458 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
459 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
460 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
463 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
464 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
465 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
467 /* Channel searches */
468 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
469 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
470 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
471 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
473 /* Channel configuration */
474 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
475 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
476 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
477 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
480 { "CSMSG_USER_OPTIONS", "User Options:" },
481 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
484 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
485 { "CSMSG_PING_RESPONSE", "Pong!" },
486 { "CSMSG_PONG_RESPONSE", "Ping!" },
487 { "CSMSG_WUT_RESPONSE", "wut" },
488 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
489 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
490 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
491 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
492 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
493 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
494 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
497 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
498 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
499 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
500 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
501 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
502 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
503 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
504 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
505 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
506 { "CSMSG_VOTE_QUESTION", "Question: %s" },
507 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
508 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
509 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
510 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
511 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
512 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
513 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
514 { "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." },
515 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
516 { "CSMSG_VOTE_VOTED", "You have already voted." },
517 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
518 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
519 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
520 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
523 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
527 #define CSMSG_ALERT_REGISTERED "%s registered to %s by %s."
528 #define CSMSG_ALERT_UNREGISTERED "%s %s"
530 /* eject_user and unban_user flags */
531 #define ACTION_KICK 0x0001
532 #define ACTION_BAN 0x0002
533 #define ACTION_ADD_BAN 0x0004
534 #define ACTION_ADD_TIMED_BAN 0x0008
535 #define ACTION_UNBAN 0x0010
536 #define ACTION_DEL_BAN 0x0020
538 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
539 #define MODELEN 40 + KEYLEN
543 #define CSFUNC_ARGS user, channel, argc, argv, cmd
545 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
546 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
547 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
548 reply("MSG_MISSING_PARAMS", argv[0]); \
552 DECLARE_LIST(dnrList, struct do_not_register *);
553 DEFINE_LIST(dnrList, struct do_not_register *)
555 #define chanserv_notice(target, format...) send_message(target , chanserv , ## format)
556 #define chanserv_oper_message(format...) do { if(chanserv_conf.oper_channel) send_channel_message(chanserv_conf.oper_channel , chanserv , ## format); } while(0)
558 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
560 struct userNode *chanserv;
563 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
564 static struct log_type *CS_LOG;
568 struct channelList support_channels;
569 struct mod_chanmode default_modes;
571 unsigned long db_backup_frequency;
572 unsigned long channel_expire_frequency;
573 unsigned long dnr_expire_frequency;
575 unsigned long invited_timeout;
577 unsigned long info_delay;
578 unsigned long adjust_delay;
579 unsigned long channel_expire_delay;
580 unsigned int nodelete_level;
582 unsigned int adjust_threshold;
583 int join_flood_threshold;
585 unsigned int greeting_length;
586 unsigned int refresh_period;
587 unsigned int giveownership_period;
589 unsigned int max_owned;
590 unsigned int max_chan_users;
591 unsigned int max_chan_bans;
592 unsigned int min_time_bans;
593 unsigned int max_userinfo_length;
595 unsigned int revoke_mode_a;
597 struct string_list *set_shows;
598 struct string_list *eightball;
599 struct string_list *old_ban_names;
601 const char *ctcp_short_ban_duration;
602 const char *ctcp_long_ban_duration;
604 const char *irc_operator_epithet;
605 const char *network_helper_epithet;
606 const char *support_helper_epithet;
608 const char *new_channel_authed;
609 const char *new_channel_unauthed;
610 const char *new_channel_msg;
612 struct chanNode *oper_channel;
617 struct userNode *user;
618 struct userNode *bot;
619 struct chanNode *channel;
621 unsigned short lowest;
622 unsigned short highest;
623 struct userData **users;
624 struct helpfile_table table;
629 struct userNode *user;
630 struct chanNode *chan;
633 enum note_access_type
635 NOTE_SET_CHANNEL_ACCESS,
636 NOTE_SET_CHANNEL_SETTER,
640 enum note_visible_type
643 NOTE_VIS_CHANNEL_USERS,
649 enum note_access_type set_access_type;
651 unsigned int min_opserv;
652 unsigned short min_ulevel;
654 enum note_visible_type visible_type;
655 unsigned int max_length;
662 struct note_type *type;
663 char setter[NICKSERV_HANDLE_LEN+1];
667 static unsigned int registered_channels;
668 static unsigned int banCount;
670 static const struct {
673 unsigned short level;
676 { "peon", "Peon", UL_PEON, '+' },
677 { "op", "Op", UL_OP, '@' },
678 { "master", "Master", UL_MASTER, '%' },
679 { "coowner", "Coowner", UL_COOWNER, '*' },
680 { "owner", "Owner", UL_OWNER, '!' },
681 { "helper", "BUG:", UL_HELPER, 'X' }
684 static const struct {
687 unsigned short default_value;
688 unsigned int old_idx;
689 unsigned int old_flag;
690 unsigned short flag_value;
692 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
693 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
694 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
695 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
696 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
697 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
698 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
699 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
700 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
701 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
702 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
703 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
706 struct charOptionValues {
709 } protectValues[] = {
710 { 'a', "CSMSG_PROTECT_ALL" },
711 { 'e', "CSMSG_PROTECT_EQUAL" },
712 { 'l', "CSMSG_PROTECT_LOWER" },
713 { 'n', "CSMSG_PROTECT_NONE" }
715 { 'd', "CSMSG_TOYS_DISABLED" },
716 { 'n', "CSMSG_TOYS_PRIVATE" },
717 { 'p', "CSMSG_TOYS_PUBLIC" }
718 }, topicRefreshValues[] = {
719 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
720 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
721 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
722 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
723 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
724 }, ctcpReactionValues[] = {
725 { 'k', "CSMSG_CTCPREACTION_KICK" },
726 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
727 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
728 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
731 static const struct {
735 unsigned int old_idx;
737 struct charOptionValues *values;
739 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
740 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
741 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
742 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
745 struct userData *helperList;
746 struct chanData *channelList;
747 static struct module *chanserv_module;
748 static unsigned int userCount;
750 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
751 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
752 static void unregister_channel(struct chanData *channel, const char *reason);
755 user_level_from_name(const char *name, unsigned short clamp_level)
757 unsigned int level = 0, ii;
759 level = strtoul(name, NULL, 10);
760 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
761 if(!irccasecmp(name, accessLevels[ii].name))
762 level = accessLevels[ii].level;
763 if(level > clamp_level)
769 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
772 *minl = strtoul(arg, &sep, 10);
780 *maxl = strtoul(sep+1, &sep, 10);
788 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
790 struct userData *uData, **head;
792 if(!channel || !handle)
795 if(override && HANDLE_FLAGGED(handle, HELPING)
796 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
798 for(uData = helperList;
799 uData && uData->handle != handle;
800 uData = uData->next);
804 uData = calloc(1, sizeof(struct userData));
805 uData->handle = handle;
807 uData->access = UL_HELPER;
813 uData->next = helperList;
815 helperList->prev = uData;
823 for(uData = channel->users; uData; uData = uData->next)
824 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
827 head = &(channel->users);
830 if(uData && (uData != *head))
832 /* Shuffle the user to the head of whatever list he was in. */
834 uData->next->prev = uData->prev;
836 uData->prev->next = uData->next;
842 (**head).prev = uData;
849 /* Returns non-zero if user has at least the minimum access.
850 * exempt_owner is set when handling !set, so the owner can set things
853 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
855 struct userData *uData;
856 struct chanData *cData = channel->channel_info;
857 unsigned short minimum = cData->lvlOpts[opt];
860 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
863 if(minimum <= uData->access)
865 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
870 /* Scan for other users authenticated to the same handle
871 still in the channel. If so, keep them listed as present.
873 user is optional, if not null, it skips checking that userNode
874 (for the handle_part function) */
876 scan_user_presence(struct userData *uData, struct userNode *user)
880 if(IsSuspended(uData->channel)
881 || IsUserSuspended(uData)
882 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
894 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
896 unsigned int eflags, argc;
898 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
900 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
901 if(!channel->channel_info
902 || IsSuspended(channel->channel_info)
904 || !ircncasecmp(text, "ACTION ", 7))
906 /* Figure out the minimum level needed to CTCP the channel */
907 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
909 /* We need to enforce against them; do so. */
911 argv[0] = (char*)text;
912 argv[1] = user->nick;
914 if(GetUserMode(channel, user))
915 eflags |= ACTION_KICK;
916 switch(channel->channel_info->chOpts[chCTCPReaction]) {
917 default: case 'k': /* just do the kick */ break;
919 eflags |= ACTION_BAN;
922 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
923 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
926 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
927 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
930 argv[argc++] = bad_ctcp_reason;
931 eject_user(chanserv, channel, argc, argv, NULL, eflags);
935 chanserv_create_note_type(const char *name)
937 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
938 strcpy(ntype->name, name);
940 dict_insert(note_types, ntype->name, ntype);
945 free_vote_options(void *data)
947 struct vote_option *vOpt = data;
949 free(vOpt->option_str);
954 chanserv_deref_note_type(void *data)
956 struct note_type *ntype = data;
958 if(--ntype->refs > 0)
964 chanserv_flush_note_type(struct note_type *ntype)
966 struct chanData *cData;
967 for(cData = channelList; cData; cData = cData->next)
968 dict_remove(cData->notes, ntype->name);
972 chanserv_truncate_notes(struct note_type *ntype)
974 struct chanData *cData;
976 unsigned int size = sizeof(*note) + ntype->max_length;
978 for(cData = channelList; cData; cData = cData->next) {
979 note = dict_find(cData->notes, ntype->name, NULL);
982 if(strlen(note->note) <= ntype->max_length)
984 dict_remove2(cData->notes, ntype->name, 1);
985 note = realloc(note, size);
986 note->note[ntype->max_length] = 0;
987 dict_insert(cData->notes, ntype->name, note);
991 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
994 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
997 unsigned int len = strlen(text);
999 if(len > type->max_length) len = type->max_length;
1000 note = calloc(1, sizeof(*note) + len);
1002 strncpy(note->setter, setter, sizeof(note->setter)-1);
1003 memcpy(note->note, text, len);
1004 note->note[len] = 0;
1005 dict_insert(channel->notes, type->name, note);
1011 chanserv_free_note(void *data)
1013 struct note *note = data;
1015 chanserv_deref_note_type(note->type);
1016 assert(note->type->refs > 0); /* must use delnote to remove the type */
1020 static MODCMD_FUNC(cmd_createnote) {
1021 struct note_type *ntype;
1022 unsigned int arg = 1, existed = 0, max_length;
1024 if((ntype = dict_find(note_types, argv[1], NULL)))
1027 ntype = chanserv_create_note_type(argv[arg]);
1028 if(!irccasecmp(argv[++arg], "privileged"))
1031 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1032 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1034 else if(!irccasecmp(argv[arg], "channel"))
1036 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1039 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1042 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1043 ntype->set_access.min_ulevel = ulvl;
1045 else if(!irccasecmp(argv[arg], "setter"))
1047 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1051 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1055 if(!irccasecmp(argv[++arg], "privileged"))
1056 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1057 else if(!irccasecmp(argv[arg], "channel_users"))
1058 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1059 else if(!irccasecmp(argv[arg], "all"))
1060 ntype->visible_type = NOTE_VIS_ALL;
1062 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1066 if((arg+1) >= argc) {
1067 reply("MSG_MISSING_PARAMS", argv[0]);
1070 max_length = strtoul(argv[++arg], NULL, 0);
1071 if(max_length < 20 || max_length > 450)
1073 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1076 if(existed && (max_length < ntype->max_length))
1078 ntype->max_length = max_length;
1079 chanserv_truncate_notes(ntype);
1081 ntype->max_length = max_length;
1084 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1086 reply("CSMSG_NOTE_CREATED", ntype->name);
1091 dict_remove(note_types, ntype->name);
1095 static MODCMD_FUNC(cmd_removenote) {
1096 struct note_type *ntype;
1099 ntype = dict_find(note_types, argv[1], NULL);
1100 force = (argc > 2) && !irccasecmp(argv[2], "force");
1103 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1110 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1113 chanserv_flush_note_type(ntype);
1115 dict_remove(note_types, argv[1]);
1116 reply("CSMSG_NOTE_DELETED", argv[1]);
1121 chanserv_expire_channel(void *data)
1123 struct chanData *channel = data;
1124 char reason[MAXLEN];
1125 sprintf(reason, "channel expired.");
1126 channel->expiry = 0;
1127 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1128 unregister_channel(channel, reason);
1131 static MODCMD_FUNC(chan_opt_expire)
1133 struct chanData *cData = channel->channel_info;
1134 unsigned long value = cData->expiry;
1138 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1140 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1143 unsigned long expiry,duration;
1145 /* The two directions can have different ACLs. */
1146 if(!strcmp(argv[1], "0"))
1148 else if((duration = ParseInterval(argv[1])))
1149 expiry = now + duration;
1152 reply("MSG_INVALID_DURATION", argv[1]);
1156 if (expiry != value)
1160 timeq_del(value, chanserv_expire_channel, cData, 0);
1163 cData->expiry = value;
1166 timeq_add(expiry, chanserv_expire_channel, cData);
1171 if(cData->expiry > now) {
1172 char expirestr[INTERVALLEN];
1173 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1175 reply("CSMSG_SET_EXPIRE_OFF");
1180 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1184 if(orig->modes_set & change->modes_clear)
1186 if(orig->modes_clear & change->modes_set)
1188 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1189 && strcmp(orig->new_key, change->new_key))
1191 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1192 && (orig->new_limit != change->new_limit))
1197 static char max_length_text[MAXLEN+1][16];
1199 static struct helpfile_expansion
1200 chanserv_expand_variable(const char *variable)
1202 struct helpfile_expansion exp;
1204 if(!irccasecmp(variable, "notes"))
1207 exp.type = HF_TABLE;
1208 exp.value.table.length = 1;
1209 exp.value.table.width = 3;
1210 exp.value.table.flags = 0;
1211 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1212 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1213 exp.value.table.contents[0][0] = "Note Type";
1214 exp.value.table.contents[0][1] = "Visibility";
1215 exp.value.table.contents[0][2] = "Max Length";
1216 for(it=dict_first(note_types); it; it=iter_next(it))
1218 struct note_type *ntype = iter_data(it);
1221 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1222 row = exp.value.table.length++;
1223 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1224 exp.value.table.contents[row][0] = ntype->name;
1225 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1226 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1228 if(!max_length_text[ntype->max_length][0])
1229 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1230 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1235 exp.type = HF_STRING;
1236 exp.value.str = NULL;
1240 static struct chanData*
1241 register_channel(struct chanNode *cNode, char *registrar)
1243 struct chanData *channel;
1244 enum levelOption lvlOpt;
1245 enum charOption chOpt;
1248 channel = calloc(1, sizeof(struct chanData));
1250 channel->notes = dict_new();
1251 dict_set_free_data(channel->notes, chanserv_free_note);
1253 channel->registrar = strdup(registrar);
1254 channel->registered = now;
1255 channel->visited = now;
1256 channel->limitAdjusted = now;
1257 channel->ownerTransfer = now;
1258 channel->flags = CHANNEL_DEFAULT_FLAGS;
1259 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1260 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1261 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1262 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1263 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1264 channel->advtopic[i] = NULL;
1266 channel->prev = NULL;
1267 channel->next = channelList;
1270 channelList->prev = channel;
1271 channelList = channel;
1272 registered_channels++;
1274 channel->channel = cNode;
1276 cNode->channel_info = channel;
1278 channel->vote = NULL;
1283 static struct userData*
1284 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1286 struct userData *ud;
1288 if(access_level > UL_OWNER)
1291 ud = calloc(1, sizeof(*ud));
1292 ud->channel = channel;
1293 ud->handle = handle;
1295 ud->access = access_level;
1296 ud->info = info ? strdup(info) : NULL;
1299 ud->next = channel->users;
1301 channel->users->prev = ud;
1302 channel->users = ud;
1304 channel->userCount++;
1308 ud->u_next = ud->handle->channels;
1310 ud->u_next->u_prev = ud;
1311 ud->handle->channels = ud;
1317 del_channel_user(struct userData *user, int do_gc)
1319 struct chanData *channel = user->channel;
1321 channel->userCount--;
1325 user->prev->next = user->next;
1327 channel->users = user->next;
1329 user->next->prev = user->prev;
1332 user->u_prev->u_next = user->u_next;
1334 user->handle->channels = user->u_next;
1336 user->u_next->u_prev = user->u_prev;
1340 if(do_gc && !channel->users && !IsProtected(channel)) {
1341 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1342 unregister_channel(channel, "lost all users.");
1346 static void expire_ban(void *data);
1349 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1352 unsigned int ii, l1, l2;
1357 bd = malloc(sizeof(struct banData));
1359 bd->channel = channel;
1361 bd->triggered = triggered;
1362 bd->expires = expires;
1364 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1366 extern const char *hidden_host_suffix;
1367 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1371 l2 = strlen(old_name);
1374 if(irccasecmp(mask + l1 - l2, old_name))
1376 new_mask = alloca(MAXLEN);
1377 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1380 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1382 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1383 bd->reason = strdup(reason);
1386 timeq_add(expires, expire_ban, bd);
1389 bd->next = channel->bans;
1391 channel->bans->prev = bd;
1393 channel->banCount++;
1400 del_channel_ban(struct banData *ban)
1402 ban->channel->banCount--;
1406 ban->prev->next = ban->next;
1408 ban->channel->bans = ban->next;
1411 ban->next->prev = ban->prev;
1414 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1423 expire_ban(void *data)
1425 struct banData *bd = data;
1426 if(!IsSuspended(bd->channel))
1428 struct banList bans;
1429 struct mod_chanmode change;
1431 bans = bd->channel->channel->banlist;
1432 mod_chanmode_init(&change);
1433 for(ii=0; ii<bans.used; ii++)
1435 if(!strcmp(bans.list[ii]->ban, bd->mask))
1438 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1439 change.args[0].u.hostmask = bd->mask;
1440 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1446 del_channel_ban(bd);
1449 static void chanserv_expire_suspension(void *data);
1452 unregister_channel(struct chanData *channel, const char *reason)
1454 struct mod_chanmode change;
1455 char msgbuf[MAXLEN];
1458 /* After channel unregistration, the following must be cleaned
1460 - Channel information.
1463 - Channel suspension data.
1464 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1470 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1472 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1473 mod_chanmode_init(&change);
1475 change.modes_clear |= MODE_REGISTERED;
1476 if(chanserv_conf.revoke_mode_a)
1477 change.modes_clear |= MODE_ACCESS;
1478 mod_chanmode_announce(chanserv, channel->channel, &change);
1481 while(channel->users)
1482 del_channel_user(channel->users, 0);
1484 while(channel->bans)
1485 del_channel_ban(channel->bans);
1487 free(channel->topic);
1488 free(channel->registrar);
1489 free(channel->greeting);
1490 free(channel->user_greeting);
1491 free(channel->topic_mask);
1493 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1494 if(channel->advtopic[i])
1495 free(channel->advtopic[i]);
1499 channel->prev->next = channel->next;
1501 channelList = channel->next;
1504 channel->next->prev = channel->prev;
1506 if(channel->suspended)
1508 struct chanNode *cNode = channel->channel;
1509 struct suspended *suspended, *next_suspended;
1511 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1513 next_suspended = suspended->previous;
1514 free(suspended->suspender);
1515 free(suspended->reason);
1516 if(suspended->expires)
1517 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1522 cNode->channel_info = NULL;
1525 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1526 channel->channel->channel_info = NULL;
1527 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1528 dict_delete(channel->notes);
1529 if(!IsSuspended(channel))
1530 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1532 chanserv_oper_message(CSMSG_ALERT_UNREGISTERED, channel->channel->name, reason);
1533 UnlockChannel(channel->channel);
1535 registered_channels--;
1539 expire_channels(void *data)
1541 struct chanData *channel, *next;
1542 struct userData *user;
1543 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1545 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1546 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1548 for(channel = channelList; channel; channel = next)
1550 next = channel->next;
1552 /* See if the channel can be expired. */
1553 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1554 || IsProtected(channel))
1557 /* Make sure there are no high-ranking users still in the channel. */
1558 for(user=channel->users; user; user=user->next)
1559 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1564 /* Unregister the channel */
1565 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1566 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1567 unregister_channel(channel, "registration expired.");
1570 if(chanserv_conf.channel_expire_frequency && !data)
1571 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1575 expire_dnrs(UNUSED_ARG(void *data))
1577 dict_iterator_t it, next;
1578 struct do_not_register *dnr;
1580 for(it = dict_first(handle_dnrs); it; it = next)
1582 dnr = iter_data(it);
1583 next = iter_next(it);
1584 if(dnr->expires && dnr->expires <= now)
1585 dict_remove(handle_dnrs, dnr->chan_name + 1);
1587 for(it = dict_first(plain_dnrs); it; it = next)
1589 dnr = iter_data(it);
1590 next = iter_next(it);
1591 if(dnr->expires && dnr->expires <= now)
1592 dict_remove(plain_dnrs, dnr->chan_name + 1);
1594 for(it = dict_first(mask_dnrs); it; it = next)
1596 dnr = iter_data(it);
1597 next = iter_next(it);
1598 if(dnr->expires && dnr->expires <= now)
1599 dict_remove(mask_dnrs, dnr->chan_name + 1);
1602 if(chanserv_conf.dnr_expire_frequency)
1603 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1607 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1609 char protect = channel->chOpts[chProtect];
1610 struct userData *cs_victim, *cs_aggressor;
1612 /* Don't protect if no one is to be protected, someone is attacking
1613 himself, or if the aggressor is an IRC Operator. */
1614 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1617 /* Don't protect if the victim isn't authenticated (because they
1618 can't be a channel user), unless we are to protect non-users
1620 cs_victim = GetChannelAccess(channel, victim->handle_info);
1621 if(protect != 'a' && !cs_victim)
1624 /* Protect if the aggressor isn't a user because at this point,
1625 the aggressor can only be less than or equal to the victim. */
1626 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1630 /* If the aggressor was a user, then the victim can't be helped. */
1637 if(cs_victim->access > cs_aggressor->access)
1642 if(cs_victim->access >= cs_aggressor->access)
1651 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1653 struct chanData *cData = channel->channel_info;
1654 struct userData *cs_victim;
1656 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1657 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1658 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1660 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1668 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1670 if(IsService(victim))
1672 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1676 if(protect_user(victim, user, channel->channel_info))
1678 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1685 static struct do_not_register *
1686 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1688 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1689 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1690 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1691 strcpy(dnr->reason, reason);
1693 dnr->expires = expires;
1694 if(dnr->chan_name[0] == '*')
1695 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1696 else if(strpbrk(dnr->chan_name, "*?"))
1697 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1699 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1703 static struct dnrList
1704 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1706 struct dnrList list;
1707 dict_iterator_t it, next;
1708 struct do_not_register *dnr;
1710 dnrList_init(&list);
1712 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1714 if(dnr->expires && dnr->expires <= now)
1715 dict_remove(handle_dnrs, handle);
1716 else if(list.used < max)
1717 dnrList_append(&list, dnr);
1720 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1722 if(dnr->expires && dnr->expires <= now)
1723 dict_remove(plain_dnrs, chan_name);
1724 else if(list.used < max)
1725 dnrList_append(&list, dnr);
1730 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1732 next = iter_next(it);
1733 if(!match_ircglob(chan_name, iter_key(it)))
1735 dnr = iter_data(it);
1736 if(dnr->expires && dnr->expires <= now)
1737 dict_remove(mask_dnrs, iter_key(it));
1739 dnrList_append(&list, dnr);
1746 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1748 struct userNode *user;
1749 char buf1[INTERVALLEN];
1750 char buf2[INTERVALLEN];
1757 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1762 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1763 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1767 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1770 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1775 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1777 struct dnrList list;
1780 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1781 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1782 dnr_print_func(list.list[ii], user);
1784 reply("CSMSG_MORE_DNRS", list.used - ii);
1789 struct do_not_register *
1790 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1792 struct dnrList list;
1793 struct do_not_register *dnr;
1795 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1796 dnr = list.used ? list.list[0] : NULL;
1801 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1803 struct do_not_register *dnr;
1804 dict_iterator_t it, next;
1805 unsigned int matches = 0;
1807 for(it = dict_first(dict); it; it = next)
1809 dnr = iter_data(it);
1810 next = iter_next(it);
1811 if(dnr->expires && dnr->expires <= now)
1813 dict_remove(dict, iter_key(it));
1816 dnr_print_func(dnr, user);
1823 static CHANSERV_FUNC(cmd_noregister)
1827 unsigned long expiry, duration;
1828 unsigned int matches;
1832 reply("CSMSG_DNR_SEARCH_RESULTS");
1833 matches = send_dnrs(user, handle_dnrs);
1834 matches += send_dnrs(user, plain_dnrs);
1835 matches += send_dnrs(user, mask_dnrs);
1837 reply("MSG_MATCH_COUNT", matches);
1839 reply("MSG_NO_MATCHES");
1845 if(!IsChannelName(target) && (*target != '*'))
1847 reply("CSMSG_NOT_DNR", target);
1855 reply("MSG_INVALID_DURATION", argv[2]);
1859 if(!strcmp(argv[2], "0"))
1861 else if((duration = ParseInterval(argv[2])))
1862 expiry = now + duration;
1865 reply("MSG_INVALID_DURATION", argv[2]);
1869 reason = unsplit_string(argv + 3, argc - 3, NULL);
1870 if((*target == '*') && !get_handle_info(target + 1))
1872 reply("MSG_HANDLE_UNKNOWN", target + 1);
1875 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1876 reply("CSMSG_NOREGISTER_CHANNEL", target);
1880 reply("CSMSG_DNR_SEARCH_RESULTS");
1882 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1884 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1886 reply("MSG_NO_MATCHES");
1890 static CHANSERV_FUNC(cmd_allowregister)
1892 const char *chan_name = argv[1];
1894 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1895 || dict_remove(plain_dnrs, chan_name)
1896 || dict_remove(mask_dnrs, chan_name))
1898 reply("CSMSG_DNR_REMOVED", chan_name);
1901 reply("CSMSG_NO_SUCH_DNR", chan_name);
1906 struct userNode *source;
1910 unsigned long min_set, max_set;
1911 unsigned long min_expires, max_expires;
1916 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1918 return !((dnr->set < search->min_set)
1919 || (dnr->set > search->max_set)
1920 || (dnr->expires < search->min_expires)
1921 || (search->max_expires
1922 && ((dnr->expires == 0)
1923 || (dnr->expires > search->max_expires)))
1924 || (search->chan_mask
1925 && !match_ircglob(dnr->chan_name, search->chan_mask))
1926 || (search->setter_mask
1927 && !match_ircglob(dnr->setter, search->setter_mask))
1928 || (search->reason_mask
1929 && !match_ircglob(dnr->reason, search->reason_mask)));
1932 static struct dnr_search *
1933 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1935 struct dnr_search *discrim;
1938 discrim = calloc(1, sizeof(*discrim));
1939 discrim->source = user;
1940 discrim->chan_mask = NULL;
1941 discrim->setter_mask = NULL;
1942 discrim->reason_mask = NULL;
1943 discrim->max_set = INT_MAX;
1944 discrim->limit = 50;
1946 for(ii=0; ii<argc; ++ii)
1950 reply("MSG_MISSING_PARAMS", argv[ii]);
1953 else if(0 == irccasecmp(argv[ii], "channel"))
1955 discrim->chan_mask = argv[++ii];
1957 else if(0 == irccasecmp(argv[ii], "setter"))
1959 discrim->setter_mask = argv[++ii];
1961 else if(0 == irccasecmp(argv[ii], "reason"))
1963 discrim->reason_mask = argv[++ii];
1965 else if(0 == irccasecmp(argv[ii], "limit"))
1967 discrim->limit = strtoul(argv[++ii], NULL, 0);
1969 else if(0 == irccasecmp(argv[ii], "set"))
1971 const char *cmp = argv[++ii];
1974 discrim->min_set = now - ParseInterval(cmp + 2);
1976 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1977 } else if(cmp[0] == '=') {
1978 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1979 } else if(cmp[0] == '>') {
1981 discrim->max_set = now - ParseInterval(cmp + 2);
1983 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1985 discrim->max_set = now - (ParseInterval(cmp) - 1);
1988 else if(0 == irccasecmp(argv[ii], "expires"))
1990 const char *cmp = argv[++ii];
1993 discrim->max_expires = now + ParseInterval(cmp + 2);
1995 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1996 } else if(cmp[0] == '=') {
1997 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1998 } else if(cmp[0] == '>') {
2000 discrim->min_expires = now + ParseInterval(cmp + 2);
2002 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2004 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2009 reply("MSG_INVALID_CRITERIA", argv[ii]);
2020 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2023 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2025 struct do_not_register *dnr;
2026 dict_iterator_t next;
2031 /* Initialize local variables. */
2034 if(discrim->chan_mask)
2036 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2037 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2041 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2043 /* Check against account-based DNRs. */
2044 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2045 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2048 else if(target_fixed)
2050 /* Check against channel-based DNRs. */
2051 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2052 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2057 /* Exhaustively search account DNRs. */
2058 for(it = dict_first(handle_dnrs); it; it = next)
2060 next = iter_next(it);
2061 dnr = iter_data(it);
2062 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2066 /* Do the same for channel DNRs. */
2067 for(it = dict_first(plain_dnrs); it; it = next)
2069 next = iter_next(it);
2070 dnr = iter_data(it);
2071 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2075 /* Do the same for wildcarded channel DNRs. */
2076 for(it = dict_first(mask_dnrs); it; it = next)
2078 next = iter_next(it);
2079 dnr = iter_data(it);
2080 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2088 dnr_remove_func(struct do_not_register *match, void *extra)
2090 struct userNode *user;
2093 chan_name = alloca(strlen(match->chan_name) + 1);
2094 strcpy(chan_name, match->chan_name);
2096 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2097 || dict_remove(plain_dnrs, chan_name)
2098 || dict_remove(mask_dnrs, chan_name))
2100 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2106 dnr_count_func(struct do_not_register *match, void *extra)
2108 return 0; (void)match; (void)extra;
2111 static MODCMD_FUNC(cmd_dnrsearch)
2113 struct dnr_search *discrim;
2114 dnr_search_func action;
2115 struct svccmd *subcmd;
2116 unsigned int matches;
2119 sprintf(buf, "dnrsearch %s", argv[1]);
2120 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2123 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2126 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2128 if(!irccasecmp(argv[1], "print"))
2129 action = dnr_print_func;
2130 else if(!irccasecmp(argv[1], "remove"))
2131 action = dnr_remove_func;
2132 else if(!irccasecmp(argv[1], "count"))
2133 action = dnr_count_func;
2136 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2140 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2144 if(action == dnr_print_func)
2145 reply("CSMSG_DNR_SEARCH_RESULTS");
2146 matches = dnr_search(discrim, action, user);
2148 reply("MSG_MATCH_COUNT", matches);
2150 reply("MSG_NO_MATCHES");
2156 chanserv_get_owned_count(struct handle_info *hi)
2158 struct userData *cList;
2161 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2162 if(cList->access == UL_OWNER)
2167 static CHANSERV_FUNC(cmd_register)
2169 struct handle_info *handle;
2170 struct chanData *cData;
2171 struct modeNode *mn;
2173 unsigned int new_channel, force=0;
2174 struct do_not_register *dnr;
2178 if(channel->channel_info)
2180 reply("CSMSG_ALREADY_REGGED", channel->name);
2184 if(channel->bad_channel)
2186 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2191 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2193 reply("CSMSG_MUST_BE_OPPED", channel->name);
2198 chan_name = channel->name;
2202 if((argc < 2) || !IsChannelName(argv[1]))
2204 reply("MSG_NOT_CHANNEL_NAME");
2208 if(opserv_bad_channel(argv[1]))
2210 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2215 chan_name = argv[1];
2218 if(argc >= (new_channel+2))
2220 if(!IsHelping(user))
2222 reply("CSMSG_PROXY_FORBIDDEN");
2226 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2228 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2229 dnr = chanserv_is_dnr(chan_name, handle);
2233 handle = user->handle_info;
2234 dnr = chanserv_is_dnr(chan_name, handle);
2238 if(!IsHelping(user))
2239 reply("CSMSG_DNR_CHANNEL", chan_name);
2241 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2245 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2247 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2252 channel = AddChannel(argv[1], now, NULL, NULL);
2254 cData = register_channel(channel, user->handle_info->handle);
2255 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2256 cData->modes = chanserv_conf.default_modes;
2258 cData->modes.modes_set |= MODE_REGISTERED;
2259 if (IsOffChannel(cData))
2261 mod_chanmode_announce(chanserv, channel, &cData->modes);
2265 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2266 change->args[change->argc].mode = MODE_CHANOP;
2267 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2269 mod_chanmode_announce(chanserv, channel, change);
2270 mod_chanmode_free(change);
2273 /* Initialize the channel's max user record. */
2274 cData->max = channel->members.used;
2275 cData->max_time = 0;
2277 if(handle != user->handle_info)
2278 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2280 reply("CSMSG_REG_SUCCESS", channel->name);
2282 chanserv_oper_message(CSMSG_ALERT_REGISTERED, channel->name, handle->handle, user->handle_info->handle);
2287 make_confirmation_string(struct userData *uData)
2289 static char strbuf[16];
2294 for(src = uData->handle->handle; *src; )
2295 accum = accum * 31 + toupper(*src++);
2297 for(src = uData->channel->channel->name; *src; )
2298 accum = accum * 31 + toupper(*src++);
2299 sprintf(strbuf, "%08x", accum);
2303 static CHANSERV_FUNC(cmd_unregister)
2306 char reason[MAXLEN];
2307 struct chanData *cData;
2308 struct userData *uData;
2310 cData = channel->channel_info;
2313 reply("CSMSG_NOT_REGISTERED", channel->name);
2317 uData = GetChannelUser(cData, user->handle_info);
2318 if(!uData || (uData->access < UL_OWNER))
2320 reply("CSMSG_NO_ACCESS");
2324 if(IsProtected(cData) && !IsOper(user))
2326 reply("CSMSG_UNREG_NODELETE", channel->name);
2330 if(!IsHelping(user))
2332 const char *confirm_string;
2333 if(IsSuspended(cData))
2335 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2338 confirm_string = make_confirmation_string(uData);
2339 if((argc < 2) || strcmp(argv[1], confirm_string))
2341 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2346 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2347 name = strdup(channel->name);
2348 unregister_channel(cData, reason);
2349 spamserv_cs_unregister(user, channel, manually, "unregistered");
2350 reply("CSMSG_UNREG_SUCCESS", name);
2356 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2358 extern struct userNode *spamserv;
2359 struct mod_chanmode *change;
2361 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2363 change = mod_chanmode_alloc(2);
2365 change->args[0].mode = MODE_CHANOP;
2366 change->args[0].u.member = AddChannelUser(chanserv, channel);
2367 change->args[1].mode = MODE_CHANOP;
2368 change->args[1].u.member = AddChannelUser(spamserv, channel);
2372 change = mod_chanmode_alloc(1);
2374 change->args[0].mode = MODE_CHANOP;
2375 change->args[0].u.member = AddChannelUser(chanserv, channel);
2378 mod_chanmode_announce(chanserv, channel, change);
2379 mod_chanmode_free(change);
2382 static CHANSERV_FUNC(cmd_move)
2384 struct mod_chanmode change;
2385 struct chanNode *target;
2386 struct modeNode *mn;
2387 struct userData *uData;
2388 char reason[MAXLEN];
2389 struct do_not_register *dnr;
2390 int chanserv_join = 0, spamserv_join;
2394 if(IsProtected(channel->channel_info))
2396 reply("CSMSG_MOVE_NODELETE", channel->name);
2400 if(!IsChannelName(argv[1]))
2402 reply("MSG_NOT_CHANNEL_NAME");
2406 if(opserv_bad_channel(argv[1]))
2408 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2412 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2414 for(uData = channel->channel_info->users; uData; uData = uData->next)
2416 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2418 if(!IsHelping(user))
2419 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2421 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2427 mod_chanmode_init(&change);
2428 if(!(target = GetChannel(argv[1])))
2430 target = AddChannel(argv[1], now, NULL, NULL);
2431 if(!IsSuspended(channel->channel_info))
2434 else if(target->channel_info)
2436 reply("CSMSG_ALREADY_REGGED", target->name);
2439 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2440 && !IsHelping(user))
2442 reply("CSMSG_MUST_BE_OPPED", target->name);
2445 else if(!IsSuspended(channel->channel_info))
2450 /* Clear MODE_REGISTERED from old channel, add it to new. */
2452 change.modes_clear = MODE_REGISTERED;
2453 mod_chanmode_announce(chanserv, channel, &change);
2454 change.modes_clear = 0;
2455 change.modes_set = MODE_REGISTERED;
2456 mod_chanmode_announce(chanserv, target, &change);
2459 /* Move the channel_info to the target channel; it
2460 shouldn't be necessary to clear timeq callbacks
2461 for the old channel. */
2462 target->channel_info = channel->channel_info;
2463 target->channel_info->channel = target;
2464 channel->channel_info = NULL;
2466 /* Check whether users are present in the new channel. */
2467 for(uData = target->channel_info->users; uData; uData = uData->next)
2468 scan_user_presence(uData, NULL);
2470 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2473 ss_cs_join_channel(target, spamserv_join);
2475 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2476 if(!IsSuspended(target->channel_info))
2478 char reason2[MAXLEN];
2479 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2480 DelChannelUser(chanserv, channel, reason2, 0);
2482 UnlockChannel(channel);
2483 LockChannel(target);
2484 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2485 reply("CSMSG_MOVE_SUCCESS", target->name);
2490 merge_users(struct chanData *source, struct chanData *target)
2492 struct userData *suData, *tuData, *next;
2498 /* Insert the source's users into the scratch area. */
2499 for(suData = source->users; suData; suData = suData->next)
2500 dict_insert(merge, suData->handle->handle, suData);
2502 /* Iterate through the target's users, looking for
2503 users common to both channels. The lower access is
2504 removed from either the scratch area or target user
2506 for(tuData = target->users; tuData; tuData = next)
2508 struct userData *choice;
2510 next = tuData->next;
2512 /* If a source user exists with the same handle as a target
2513 channel's user, resolve the conflict by removing one. */
2514 suData = dict_find(merge, tuData->handle->handle, NULL);
2518 /* Pick the data we want to keep. */
2519 /* If the access is the same, use the later seen time. */
2520 if(suData->access == tuData->access)
2521 choice = (suData->seen > tuData->seen) ? suData : tuData;
2522 else /* Otherwise, keep the higher access level. */
2523 choice = (suData->access > tuData->access) ? suData : tuData;
2524 /* Use the later seen time. */
2525 if(suData->seen < tuData->seen)
2526 suData->seen = tuData->seen;
2528 tuData->seen = suData->seen;
2530 /* Remove the user that wasn't picked. */
2531 if(choice == tuData)
2533 dict_remove(merge, suData->handle->handle);
2534 del_channel_user(suData, 0);
2537 del_channel_user(tuData, 0);
2540 /* Move the remaining users to the target channel. */
2541 for(it = dict_first(merge); it; it = iter_next(it))
2543 suData = iter_data(it);
2545 /* Insert the user into the target channel's linked list. */
2546 suData->prev = NULL;
2547 suData->next = target->users;
2548 suData->channel = target;
2551 target->users->prev = suData;
2552 target->users = suData;
2554 /* Update the user counts for the target channel; the
2555 source counts are left alone. */
2556 target->userCount++;
2558 /* Check whether the user is in the target channel. */
2559 scan_user_presence(suData, NULL);
2562 /* Possible to assert (source->users == NULL) here. */
2563 source->users = NULL;
2568 merge_bans(struct chanData *source, struct chanData *target)
2570 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2572 /* Hold on to the original head of the target ban list
2573 to avoid comparing source bans with source bans. */
2574 tFront = target->bans;
2576 /* Perform a totally expensive O(n*m) merge, ick. */
2577 for(sbData = source->bans; sbData; sbData = sNext)
2579 /* Flag to track whether the ban's been moved
2580 to the destination yet. */
2583 /* Possible to assert (sbData->prev == NULL) here. */
2584 sNext = sbData->next;
2586 for(tbData = tFront; tbData; tbData = tNext)
2588 tNext = tbData->next;
2590 /* Perform two comparisons between each source
2591 and target ban, conflicts are resolved by
2592 keeping the broader ban and copying the later
2593 expiration and triggered time. */
2594 if(match_ircglobs(tbData->mask, sbData->mask))
2596 /* There is a broader ban in the target channel that
2597 overrides one in the source channel; remove the
2598 source ban and break. */
2599 if(sbData->expires > tbData->expires)
2600 tbData->expires = sbData->expires;
2601 if(sbData->triggered > tbData->triggered)
2602 tbData->triggered = sbData->triggered;
2603 del_channel_ban(sbData);
2606 else if(match_ircglobs(sbData->mask, tbData->mask))
2608 /* There is a broader ban in the source channel that
2609 overrides one in the target channel; remove the
2610 target ban, fall through and move the source over. */
2611 if(tbData->expires > sbData->expires)
2612 sbData->expires = tbData->expires;
2613 if(tbData->triggered > sbData->triggered)
2614 sbData->triggered = tbData->triggered;
2615 if(tbData == tFront)
2617 del_channel_ban(tbData);
2620 /* Source bans can override multiple target bans, so
2621 we allow a source to run through this loop multiple
2622 times, but we can only move it once. */
2627 /* Remove the source ban from the source ban list. */
2629 sbData->next->prev = sbData->prev;
2631 /* Modify the source ban's associated channel. */
2632 sbData->channel = target;
2634 /* Insert the ban into the target channel's linked list. */
2635 sbData->prev = NULL;
2636 sbData->next = target->bans;
2639 target->bans->prev = sbData;
2640 target->bans = sbData;
2642 /* Update the user counts for the target channel. */
2647 /* Possible to assert (source->bans == NULL) here. */
2648 source->bans = NULL;
2652 merge_data(struct chanData *source, struct chanData *target)
2654 /* Use more recent visited and owner-transfer time; use older
2655 * registered time. Bitwise or may_opchan. Use higher max.
2656 * Do not touch last_refresh, ban count or user counts.
2658 if(source->visited > target->visited)
2659 target->visited = source->visited;
2660 if(source->registered < target->registered)
2661 target->registered = source->registered;
2662 if(source->ownerTransfer > target->ownerTransfer)
2663 target->ownerTransfer = source->ownerTransfer;
2664 if(source->may_opchan)
2665 target->may_opchan = 1;
2666 if(source->max > target->max) {
2667 target->max = source->max;
2668 target->max_time = source->max_time;
2673 merge_channel(struct chanData *source, struct chanData *target)
2675 merge_users(source, target);
2676 merge_bans(source, target);
2677 merge_data(source, target);
2680 static CHANSERV_FUNC(cmd_merge)
2682 struct userData *target_user;
2683 struct chanNode *target;
2684 char reason[MAXLEN];
2688 /* Make sure the target channel exists and is registered to the user
2689 performing the command. */
2690 if(!(target = GetChannel(argv[1])))
2692 reply("MSG_INVALID_CHANNEL");
2696 if(!target->channel_info)
2698 reply("CSMSG_NOT_REGISTERED", target->name);
2702 if(IsProtected(channel->channel_info))
2704 reply("CSMSG_MERGE_NODELETE");
2708 if(IsSuspended(target->channel_info))
2710 reply("CSMSG_MERGE_SUSPENDED");
2714 if(channel == target)
2716 reply("CSMSG_MERGE_SELF");
2720 target_user = GetChannelUser(target->channel_info, user->handle_info);
2721 if(!target_user || (target_user->access < UL_OWNER))
2723 reply("CSMSG_MERGE_NOT_OWNER");
2727 /* Merge the channel structures and associated data. */
2728 merge_channel(channel->channel_info, target->channel_info);
2729 spamserv_cs_move_merge(user, channel, target, 0);
2730 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2731 unregister_channel(channel->channel_info, reason);
2732 reply("CSMSG_MERGE_SUCCESS", target->name);
2736 static CHANSERV_FUNC(cmd_opchan)
2738 struct mod_chanmode change;
2739 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2741 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2744 channel->channel_info->may_opchan = 0;
2745 mod_chanmode_init(&change);
2747 change.args[0].mode = MODE_CHANOP;
2748 change.args[0].u.member = GetUserMode(channel, chanserv);
2749 if(!change.args[0].u.member)
2751 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2754 mod_chanmode_announce(chanserv, channel, &change);
2755 reply("CSMSG_OPCHAN_DONE", channel->name);
2759 static CHANSERV_FUNC(cmd_adduser)
2761 struct userData *actee;
2762 struct userData *actor, *real_actor;
2763 struct handle_info *handle;
2764 unsigned short access_level, override = 0;
2768 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2770 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2774 access_level = user_level_from_name(argv[2], UL_OWNER);
2777 reply("CSMSG_INVALID_ACCESS", argv[2]);
2781 actor = GetChannelUser(channel->channel_info, user->handle_info);
2782 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2784 if(actor->access <= access_level)
2786 reply("CSMSG_NO_BUMP_ACCESS");
2790 /* Trying to add someone with equal/more access? */
2791 if (!real_actor || real_actor->access <= access_level)
2792 override = CMD_LOG_OVERRIDE;
2794 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2797 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2799 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2803 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2804 scan_user_presence(actee, NULL);
2805 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2806 return 1 | override;
2809 static CHANSERV_FUNC(cmd_clvl)
2811 struct handle_info *handle;
2812 struct userData *victim;
2813 struct userData *actor, *real_actor;
2814 unsigned short new_access, override = 0;
2815 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2819 actor = GetChannelUser(channel->channel_info, user->handle_info);
2820 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2822 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2825 if(handle == user->handle_info && !privileged)
2827 reply("CSMSG_NO_SELF_CLVL");
2831 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2833 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2837 if(actor->access <= victim->access && !privileged)
2839 reply("MSG_USER_OUTRANKED", handle->handle);
2843 new_access = user_level_from_name(argv[2], UL_OWNER);
2847 reply("CSMSG_INVALID_ACCESS", argv[2]);
2851 if(new_access >= actor->access && !privileged)
2853 reply("CSMSG_NO_BUMP_ACCESS");
2857 /* Trying to clvl a equal/higher user? */
2858 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2859 override = CMD_LOG_OVERRIDE;
2860 /* Trying to clvl someone to equal/higher access? */
2861 if(!real_actor || new_access >= real_actor->access)
2862 override = CMD_LOG_OVERRIDE;
2863 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2864 * If they lower their own access it's not a big problem.
2867 victim->access = new_access;
2868 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2869 return 1 | override;
2872 static CHANSERV_FUNC(cmd_deluser)
2874 struct handle_info *handle;
2875 struct userData *victim;
2876 struct userData *actor, *real_actor;
2877 unsigned short access_level, override = 0;
2882 actor = GetChannelUser(channel->channel_info, user->handle_info);
2883 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2885 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2888 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2890 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2896 access_level = user_level_from_name(argv[1], UL_OWNER);
2899 reply("CSMSG_INVALID_ACCESS", argv[1]);
2902 if(access_level != victim->access)
2904 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2910 access_level = victim->access;
2913 if((actor->access <= victim->access) && !IsHelping(user))
2915 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2919 /* If people delete themselves it is an override, but they
2920 * could've used deleteme so we don't log it as an override
2922 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2923 override = CMD_LOG_OVERRIDE;
2925 chan_name = strdup(channel->name);
2926 del_channel_user(victim, 1);
2927 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2929 return 1 | override;
2933 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2935 struct userData *actor, *real_actor, *uData, *next;
2936 unsigned int override = 0;
2938 actor = GetChannelUser(channel->channel_info, user->handle_info);
2939 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2941 if(min_access > max_access)
2943 reply("CSMSG_BAD_RANGE", min_access, max_access);
2947 if(actor->access <= max_access)
2949 reply("CSMSG_NO_ACCESS");
2953 if(!real_actor || real_actor->access <= max_access)
2954 override = CMD_LOG_OVERRIDE;
2956 for(uData = channel->channel_info->users; uData; uData = next)
2960 if((uData->access >= min_access)
2961 && (uData->access <= max_access)
2962 && match_ircglob(uData->handle->handle, mask))
2963 del_channel_user(uData, 1);
2966 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2967 return 1 | override;
2970 static CHANSERV_FUNC(cmd_mdelowner)
2972 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2975 static CHANSERV_FUNC(cmd_mdelcoowner)
2977 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2980 static CHANSERV_FUNC(cmd_mdelmaster)
2982 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2985 static CHANSERV_FUNC(cmd_mdelop)
2987 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2990 static CHANSERV_FUNC(cmd_mdelpeon)
2992 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2996 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2998 struct banData *bData, *next;
2999 char interval[INTERVALLEN];
3001 unsigned long limit;
3004 limit = now - duration;
3005 for(bData = channel->channel_info->bans; bData; bData = next)
3009 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3012 del_channel_ban(bData);
3016 intervalString(interval, duration, user->handle_info);
3017 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3022 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3024 struct userData *actor, *uData, *next;
3025 char interval[INTERVALLEN];
3027 unsigned long limit;
3029 actor = GetChannelUser(channel->channel_info, user->handle_info);
3030 if(min_access > max_access)
3032 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3036 if(!actor || actor->access <= max_access)
3038 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3043 limit = now - duration;
3044 for(uData = channel->channel_info->users; uData; uData = next)
3048 if((uData->seen > limit)
3050 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3053 if(((uData->access >= min_access) && (uData->access <= max_access))
3054 || (!max_access && (uData->access < actor->access)))
3056 del_channel_user(uData, 1);
3064 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3066 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3070 static CHANSERV_FUNC(cmd_trim)
3072 unsigned long duration;
3073 unsigned short min_level, max_level;
3078 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3079 duration = ParseInterval(argv[2]);
3082 reply("CSMSG_CANNOT_TRIM");
3086 if(!irccasecmp(argv[1], "bans"))
3088 cmd_trim_bans(user, channel, duration);
3091 else if(!irccasecmp(argv[1], "users"))
3093 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3096 else if(parse_level_range(&min_level, &max_level, argv[1]))
3098 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3101 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3103 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3108 reply("CSMSG_INVALID_TRIM", argv[1]);
3113 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3114 to the user. cmd_all takes advantage of this. */
3115 static CHANSERV_FUNC(cmd_up)
3117 struct mod_chanmode change;
3118 struct userData *uData;
3121 mod_chanmode_init(&change);
3123 change.args[0].u.member = GetUserMode(channel, user);
3124 if(!change.args[0].u.member)
3127 reply("MSG_CHANNEL_ABSENT", channel->name);
3131 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3135 reply("CSMSG_GODMODE_UP", argv[0]);
3138 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3140 change.args[0].mode = MODE_CHANOP;
3141 errmsg = "CSMSG_ALREADY_OPPED";
3143 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3145 change.args[0].mode = MODE_VOICE;
3146 errmsg = "CSMSG_ALREADY_VOICED";
3151 reply("CSMSG_NO_ACCESS");
3154 change.args[0].mode &= ~change.args[0].u.member->modes;
3155 if(!change.args[0].mode)
3158 reply(errmsg, channel->name);
3161 modcmd_chanmode_announce(&change);
3165 static CHANSERV_FUNC(cmd_down)
3167 struct mod_chanmode change;
3169 mod_chanmode_init(&change);
3171 change.args[0].u.member = GetUserMode(channel, user);
3172 if(!change.args[0].u.member)
3175 reply("MSG_CHANNEL_ABSENT", channel->name);
3179 if(!change.args[0].u.member->modes)
3182 reply("CSMSG_ALREADY_DOWN", channel->name);
3186 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3187 modcmd_chanmode_announce(&change);
3191 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)
3193 struct userData *cList;
3195 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3197 if(IsSuspended(cList->channel)
3198 || IsUserSuspended(cList)
3199 || !GetUserMode(cList->channel->channel, user))
3202 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3208 static CHANSERV_FUNC(cmd_upall)
3210 return cmd_all(CSFUNC_ARGS, cmd_up);
3213 static CHANSERV_FUNC(cmd_downall)
3215 return cmd_all(CSFUNC_ARGS, cmd_down);
3218 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3219 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3222 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)
3224 unsigned int ii, valid;
3225 struct userNode *victim;
3226 struct mod_chanmode *change;
3228 change = mod_chanmode_alloc(argc - 1);
3230 for(ii=valid=0; ++ii < argc; )
3232 if(!(victim = GetUserH(argv[ii])))
3234 change->args[valid].mode = mode;
3235 change->args[valid].u.member = GetUserMode(channel, victim);
3236 if(!change->args[valid].u.member)
3238 if(validate && !validate(user, channel, victim))
3243 change->argc = valid;
3244 if(valid < (argc-1))
3245 reply("CSMSG_PROCESS_FAILED");
3248 modcmd_chanmode_announce(change);
3249 reply(action, channel->name);
3251 mod_chanmode_free(change);
3255 static CHANSERV_FUNC(cmd_op)
3257 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3260 static CHANSERV_FUNC(cmd_deop)
3262 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3265 static CHANSERV_FUNC(cmd_voice)
3267 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3270 static CHANSERV_FUNC(cmd_devoice)
3272 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3275 static CHANSERV_FUNC(cmd_opme)
3277 struct mod_chanmode change;
3280 mod_chanmode_init(&change);
3282 change.args[0].u.member = GetUserMode(channel, user);
3283 if(!change.args[0].u.member)
3286 reply("MSG_CHANNEL_ABSENT", channel->name);
3290 struct devnull_class *devnull;
3291 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3293 change.args[0].mode = MODE_CHANOP;
3294 errmsg = "CSMSG_ALREADY_OPPED";
3299 reply("CSMSG_NO_ACCESS");
3302 change.args[0].mode &= ~change.args[0].u.member->modes;
3303 if(!change.args[0].mode)
3306 reply(errmsg, channel->name);
3309 modcmd_chanmode_announce(&change);
3314 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3320 for(ii=0; ii<channel->members.used; ii++)
3322 struct modeNode *mn = channel->members.list[ii];
3324 if(IsService(mn->user))
3327 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3330 if(protect_user(mn->user, user, channel->channel_info))
3334 victims[(*victimCount)++] = mn;
3340 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3342 struct userNode *victim;
3343 struct modeNode **victims;
3344 unsigned int offset, n, victimCount, duration = 0;
3345 char *reason = "Bye.", *ban, *name;
3346 char interval[INTERVALLEN];
3348 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3349 REQUIRE_PARAMS(offset);
3350 if(argc > offset && IsNetServ(user))
3352 if(*argv[offset] == '$') {
3353 struct userNode *hib;
3354 const char *accountnameb = argv[offset] + 1;
3355 if(!(hib = GetUserH(accountnameb)))
3357 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3366 reason = unsplit_string(argv + offset, argc - offset, NULL);
3367 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3369 /* Truncate the reason to a length of TOPICLEN, as
3370 the ircd does; however, leave room for an ellipsis
3371 and the kicker's nick. */
3372 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3376 if((victim = GetUserH(argv[1])))
3378 victims = alloca(sizeof(victims[0]));
3379 victims[0] = GetUserMode(channel, victim);
3380 /* XXX: The comparison with ACTION_KICK is just because all
3381 * other actions can work on users outside the channel, and we
3382 * want to allow those (e.g. unbans) in that case. If we add
3383 * some other ejection action for in-channel users, change
3385 victimCount = victims[0] ? 1 : 0;
3387 if(IsService(victim))
3389 reply("MSG_SERVICE_IMMUNE", victim->nick);
3393 if((action == ACTION_KICK) && !victimCount)
3395 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3399 if(protect_user(victim, user, channel->channel_info))
3401 reply("CSMSG_USER_PROTECTED", victim->nick);
3405 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3406 name = victim->nick;
3408 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3410 struct handle_info *hi;
3411 extern const char *titlehost_suffix;
3412 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3413 const char *accountname = argv[1] + 1;
3415 if(!(hi = get_handle_info(accountname)))
3417 reply("MSG_HANDLE_UNKNOWN", accountname);
3421 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3422 victims = alloca(sizeof(victims[0]) * channel->members.used);
3424 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3426 reply("CSMSG_MASK_PROTECTED", banmask);
3430 if((action == ACTION_KICK) && (victimCount == 0))
3432 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3436 name = ban = strdup(banmask);
3440 if(!is_ircmask(argv[1]))
3442 reply("MSG_NICK_UNKNOWN", argv[1]);
3446 victims = alloca(sizeof(victims[0]) * channel->members.used);
3448 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3450 reply("CSMSG_MASK_PROTECTED", argv[1]);
3454 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3456 reply("CSMSG_LAME_MASK", argv[1]);
3460 if((action == ACTION_KICK) && (victimCount == 0))
3462 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3466 name = ban = strdup(argv[1]);
3469 /* Truncate the ban in place if necessary; we must ensure
3470 that 'ban' is a valid ban mask before sanitizing it. */
3471 sanitize_ircmask(ban);
3473 if(action & ACTION_ADD_BAN)
3475 struct banData *bData, *next;
3477 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3479 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3484 if(action & ACTION_ADD_TIMED_BAN)
3486 duration = ParseInterval(argv[2]);
3488 if(duration < chanserv_conf.min_time_bans)
3490 reply("CSMSG_DURATION_TOO_LOW");
3494 else if(duration > (86400 * 365 * 2))
3496 reply("CSMSG_DURATION_TOO_HIGH");
3502 for(bData = channel->channel_info->bans; bData; bData = next)
3504 if(match_ircglobs(bData->mask, ban))
3506 int exact = !irccasecmp(bData->mask, ban);
3508 /* The ban is redundant; there is already a ban
3509 with the same effect in place. */
3513 free(bData->reason);
3514 bData->reason = strdup(reason);
3515 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3517 reply("CSMSG_REASON_CHANGE", ban);
3521 if(exact && bData->expires)
3525 /* If the ban matches an existing one exactly,
3526 extend the expiration time if the provided
3527 duration is longer. */
3528 if(duration && (now + duration > bData->expires))
3530 bData->expires = now + duration;
3541 /* Delete the expiration timeq entry and
3542 requeue if necessary. */
3543 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3546 timeq_add(bData->expires, expire_ban, bData);
3550 /* automated kickban */
3553 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3555 reply("CSMSG_BAN_ADDED", name, channel->name);
3561 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3568 if(match_ircglobs(ban, bData->mask))
3570 /* The ban we are adding makes previously existing
3571 bans redundant; silently remove them. */
3572 del_channel_ban(bData);
3576 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);
3578 name = ban = strdup(bData->mask);
3582 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3584 extern const char *hidden_host_suffix;
3585 const char *old_name = chanserv_conf.old_ban_names->list[n];
3587 unsigned int l1, l2;
3590 l2 = strlen(old_name);
3593 if(irccasecmp(ban + l1 - l2, old_name))
3595 new_mask = malloc(MAXLEN);
3596 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3598 name = ban = new_mask;
3603 if(action & ACTION_BAN)
3605 unsigned int exists;
3606 struct mod_chanmode *change;
3608 if(channel->banlist.used >= MAXBANS)
3611 reply("CSMSG_BANLIST_FULL", channel->name);
3616 exists = ChannelBanExists(channel, ban);
3617 change = mod_chanmode_alloc(victimCount + 1);
3618 for(n = 0; n < victimCount; ++n)
3620 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3621 change->args[n].u.member = victims[n];
3625 change->args[n].mode = MODE_BAN;
3626 change->args[n++].u.hostmask = ban;
3630 modcmd_chanmode_announce(change);
3632 mod_chanmode_announce(chanserv, channel, change);
3633 mod_chanmode_free(change);
3635 if(exists && (action == ACTION_BAN))
3638 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3644 if(action & ACTION_KICK)
3646 char kick_reason[MAXLEN];
3647 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3649 for(n = 0; n < victimCount; n++)
3650 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3655 /* No response, since it was automated. */
3657 else if(action & ACTION_ADD_BAN)
3660 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3662 reply("CSMSG_BAN_ADDED", name, channel->name);
3664 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3665 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3666 else if(action & ACTION_BAN)
3667 reply("CSMSG_BAN_DONE", name, channel->name);
3668 else if(action & ACTION_KICK && victimCount)
3669 reply("CSMSG_KICK_DONE", name, channel->name);
3675 static CHANSERV_FUNC(cmd_kickban)
3677 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3680 static CHANSERV_FUNC(cmd_kick)
3682 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3685 static CHANSERV_FUNC(cmd_ban)
3687 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3690 static CHANSERV_FUNC(cmd_addban)
3692 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3695 static CHANSERV_FUNC(cmd_addtimedban)
3697 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3700 struct mod_chanmode *
3701 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3703 struct mod_chanmode *change;
3704 unsigned char *match;
3705 unsigned int ii, count;
3707 match = alloca(bans->used);
3710 for(ii = count = 0; ii < bans->used; ++ii)
3712 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3713 MATCH_USENICK | MATCH_VISIBLE);
3720 for(ii = count = 0; ii < bans->used; ++ii)
3722 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3729 change = mod_chanmode_alloc(count);
3730 for(ii = count = 0; ii < bans->used; ++ii)
3734 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3735 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3737 assert(count == change->argc);
3742 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3744 struct userNode *actee;
3750 /* may want to allow a comma delimited list of users... */
3751 if(!(actee = GetUserH(argv[1])))
3753 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3755 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3756 const char *accountname = argv[1] + 1;
3758 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3759 mask = strdup(banmask);
3761 else if(!is_ircmask(argv[1]))
3763 reply("MSG_NICK_UNKNOWN", argv[1]);
3768 mask = strdup(argv[1]);
3772 /* We don't sanitize the mask here because ircu
3774 if(action & ACTION_UNBAN)
3776 struct mod_chanmode *change;
3777 change = find_matching_bans(&channel->banlist, actee, mask);
3782 modcmd_chanmode_announce(change);
3783 for(ii = 0; ii < change->argc; ++ii)
3784 free((char*)change->args[ii].u.hostmask);
3785 mod_chanmode_free(change);
3790 if(action & ACTION_DEL_BAN)
3792 struct banData *ban, *next;
3794 ban = channel->channel_info->bans;
3798 for( ; ban && !user_matches_glob(actee, ban->mask,
3799 MATCH_USENICK | MATCH_VISIBLE);
3802 for( ; ban && !match_ircglobs(mask, ban->mask);
3807 del_channel_ban(ban);
3814 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3816 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3822 static CHANSERV_FUNC(cmd_unban)
3824 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3827 static CHANSERV_FUNC(cmd_delban)
3829 /* it doesn't necessarily have to remove the channel ban - may want
3830 to make that an option. */
3831 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3834 static CHANSERV_FUNC(cmd_unbanme)
3836 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3837 long flags = ACTION_UNBAN;
3839 /* remove permanent bans if the user has the proper access. */
3840 if(uData->access >= UL_MASTER)
3841 flags |= ACTION_DEL_BAN;
3843 argv[1] = user->nick;
3844 return unban_user(user, channel, 2, argv, cmd, flags);
3847 static CHANSERV_FUNC(cmd_unbanall)
3849 struct mod_chanmode *change;
3852 if(!channel->banlist.used)
3854 reply("CSMSG_NO_BANS", channel->name);
3858 change = mod_chanmode_alloc(channel->banlist.used);
3859 for(ii=0; ii<channel->banlist.used; ii++)
3861 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3862 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3864 modcmd_chanmode_announce(change);
3865 for(ii = 0; ii < change->argc; ++ii)
3866 free((char*)change->args[ii].u.hostmask);
3867 mod_chanmode_free(change);
3868 reply("CSMSG_BANS_REMOVED", channel->name);
3872 static CHANSERV_FUNC(cmd_open)
3874 struct mod_chanmode *change;
3877 change = find_matching_bans(&channel->banlist, user, NULL);
3879 change = mod_chanmode_alloc(0);
3880 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3881 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3882 && channel->channel_info->modes.modes_set)
3883 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3884 modcmd_chanmode_announce(change);
3885 reply("CSMSG_CHANNEL_OPENED", channel->name);
3886 for(ii = 0; ii < change->argc; ++ii)
3887 free((char*)change->args[ii].u.hostmask);
3888 mod_chanmode_free(change);
3892 static CHANSERV_FUNC(cmd_myaccess)
3894 static struct string_buffer sbuf;
3895 struct handle_info *target_handle;
3896 struct userData *uData;
3901 target_handle = user->handle_info;
3902 else if(!IsStaff(user))
3904 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3907 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3910 if(!oper_outranks(user, target_handle))
3913 if(!target_handle->channels)
3915 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3919 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3920 for(uData = target_handle->channels; uData; uData = uData->u_next)
3922 struct chanData *cData = uData->channel;
3924 unsigned int base_len;
3926 if(uData->access > UL_OWNER)
3928 if(uData->access == UL_OWNER)
3931 if(IsProtected(cData)
3932 && (target_handle != user->handle_info)
3933 && !GetTrueChannelAccess(cData, user->handle_info)
3934 && !IsNetworkHelper(user))
3937 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3938 base_len = sbuf.used;
3939 if(IsUserSuspended(uData))
3940 string_buffer_append(&sbuf, 's');
3941 if(IsUserAutoOp(uData))
3943 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3944 string_buffer_append(&sbuf, 'o');
3945 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3946 string_buffer_append(&sbuf, 'v');
3948 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3949 string_buffer_append(&sbuf, 'i');
3950 if(sbuf.used==base_len)
3953 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3955 string_buffer_append_string(&sbuf, ")]");
3956 string_buffer_append(&sbuf, '\0');
3957 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3961 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3963 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3969 static CHANSERV_FUNC(cmd_access)
3971 struct userNode *target;
3972 struct handle_info *target_handle;
3973 struct userData *uData;
3975 char prefix[MAXLEN];
3980 target_handle = target->handle_info;
3982 else if((target = GetUserH(argv[1])))
3984 target_handle = target->handle_info;
3986 else if(argv[1][0] == '*')
3988 if(!(target_handle = get_handle_info(argv[1]+1)))
3990 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3996 reply("MSG_NICK_UNKNOWN", argv[1]);
4000 assert(target || target_handle);
4002 if(target == chanserv)
4004 reply("CSMSG_IS_CHANSERV");
4012 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4017 reply("MSG_USER_AUTHENTICATE", target->nick);
4020 reply("MSG_AUTHENTICATE");
4026 const char *epithet = NULL, *type = NULL;
4029 epithet = chanserv_conf.irc_operator_epithet;
4030 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4032 else if(IsNetworkHelper(target))
4034 epithet = chanserv_conf.network_helper_epithet;
4035 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4037 else if(IsSupportHelper(target))
4039 epithet = chanserv_conf.support_helper_epithet;
4040 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4044 if(target_handle->epithet)
4045 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4047 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4049 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4053 sprintf(prefix, "%s", target_handle->handle);
4056 if(!channel->channel_info)
4058 reply("CSMSG_NOT_REGISTERED", channel->name);
4062 helping = HANDLE_FLAGGED(target_handle, HELPING)
4063 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4064 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4066 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4067 /* To prevent possible information leaks, only show infolines
4068 * if the requestor is in the channel or it's their own
4070 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4072 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4074 /* Likewise, only say it's suspended if the user has active
4075 * access in that channel or it's their own entry. */
4076 if(IsUserSuspended(uData)
4077 && (GetChannelUser(channel->channel_info, user->handle_info)
4078 || (user->handle_info == uData->handle)))
4080 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4085 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4092 def_list(struct listData *list)
4096 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4098 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4099 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4100 if(list->table.length == 1)
4102 msg = user_find_message(list->user, "MSG_NONE");
4103 send_message_type(4, list->user, list->bot, " %s", msg);
4108 userData_access_comp(const void *arg_a, const void *arg_b)
4110 const struct userData *a = *(struct userData**)arg_a;
4111 const struct userData *b = *(struct userData**)arg_b;
4113 if(a->access != b->access)
4114 res = b->access - a->access;
4116 res = irccasecmp(a->handle->handle, b->handle->handle);
4121 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4123 void (*send_list)(struct listData *);
4124 struct userData *uData;
4125 struct listData lData;
4126 unsigned int matches;
4130 lData.bot = cmd->parent->bot;
4131 lData.channel = channel;
4132 lData.lowest = lowest;
4133 lData.highest = highest;
4134 lData.search = (argc > 1) ? argv[1] : NULL;
4135 send_list = def_list;
4137 if(user->handle_info)
4139 switch(user->handle_info->userlist_style)
4141 case HI_STYLE_DEF: send_list = def_list; break;
4142 case HI_STYLE_ZOOT: send_list = def_list; break;
4146 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4148 for(uData = channel->channel_info->users; uData; uData = uData->next)
4150 if((uData->access < lowest)
4151 || (uData->access > highest)
4152 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4154 lData.users[matches++] = uData;
4156 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4158 lData.table.length = matches+1;
4159 lData.table.width = 4;
4160 lData.table.flags = TABLE_NO_FREE;
4161 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4162 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4163 lData.table.contents[0] = ary;
4166 ary[2] = "Last Seen";
4168 for(matches = 1; matches < lData.table.length; ++matches)
4170 char seen[INTERVALLEN];
4172 uData = lData.users[matches-1];
4173 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4174 lData.table.contents[matches] = ary;
4175 ary[0] = strtab(uData->access);
4176 ary[1] = uData->handle->handle;
4179 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4181 else if(!uData->seen)
4184 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4185 ary[2] = strdup(ary[2]);
4186 if(IsUserSuspended(uData))
4187 ary[3] = "Suspended";
4188 else if(HANDLE_FLAGGED(uData->handle, OPER))
4189 ary[3] = "Operator";
4190 else if(HANDLE_FLAGGED(uData->handle, HELPING))
4192 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4194 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4195 ary[3] = "Vacation";
4196 else if(HANDLE_FLAGGED(uData->handle, BOT))
4202 for(matches = 1; matches < lData.table.length; ++matches)
4204 free((char*)lData.table.contents[matches][2]);
4205 free(lData.table.contents[matches]);
4207 free(lData.table.contents[0]);
4208 free(lData.table.contents);
4209 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4213 static CHANSERV_FUNC(cmd_users)
4215 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4218 static CHANSERV_FUNC(cmd_wlist)
4220 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4223 static CHANSERV_FUNC(cmd_clist)
4225 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4228 static CHANSERV_FUNC(cmd_mlist)
4230 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4233 static CHANSERV_FUNC(cmd_olist)
4235 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4238 static CHANSERV_FUNC(cmd_plist)
4240 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4243 static CHANSERV_FUNC(cmd_bans)
4245 struct userNode *search_u = NULL;
4246 struct helpfile_table tbl;
4247 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4248 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4249 const char *msg_never, *triggered, *expires;
4250 struct banData *ban, **bans;
4254 else if(strchr(search = argv[1], '!'))
4257 search_wilds = search[strcspn(search, "?*")];
4259 else if(!(search_u = GetUserH(search)))
4260 reply("MSG_NICK_UNKNOWN", search);
4262 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4264 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4268 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4273 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4276 bans[matches++] = ban;
4281 tbl.length = matches + 1;
4282 tbl.width = 4 + timed;
4284 tbl.flags = TABLE_NO_FREE;
4285 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4286 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4287 tbl.contents[0][0] = "Mask";
4288 tbl.contents[0][1] = "Set By";
4289 tbl.contents[0][2] = "Triggered";
4292 tbl.contents[0][3] = "Expires";
4293 tbl.contents[0][4] = "Reason";
4296 tbl.contents[0][3] = "Reason";
4299 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4301 free(tbl.contents[0]);
4306 msg_never = user_find_message(user, "MSG_NEVER");
4307 for(ii = 0; ii < matches; )
4313 else if(ban->expires)
4314 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4316 expires = msg_never;
4319 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4321 triggered = msg_never;
4323 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4324 tbl.contents[ii][0] = ban->mask;
4325 tbl.contents[ii][1] = ban->owner;
4326 tbl.contents[ii][2] = strdup(triggered);
4329 tbl.contents[ii][3] = strdup(expires);
4330 tbl.contents[ii][4] = ban->reason;
4333 tbl.contents[ii][3] = ban->reason;
4335 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4336 reply("MSG_MATCH_COUNT", matches);
4337 for(ii = 1; ii < tbl.length; ++ii)
4339 free((char*)tbl.contents[ii][2]);
4341 free((char*)tbl.contents[ii][3]);
4342 free(tbl.contents[ii]);
4344 free(tbl.contents[0]);
4350 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4352 struct chanData *cData = channel->channel_info;
4353 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4355 if(cData->topic_mask)
4357 if(cData->flags & CHANNEL_ADVTOPIC)
4359 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4360 char topicmask[TOPICLEN];
4361 int skipnum, topicpos = 0;
4362 char *ptr = cData->topic_mask;
4363 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4366 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4368 topicmask[topicpos++] = '*';
4370 topicmask[topicpos++] = *ptr;
4373 topicmask[topicpos++] = *ptr;
4377 topicmask[topicpos] = 0;
4378 return !match_ircglob(new_topic, topicmask);
4381 return !match_ircglob(new_topic, cData->topic_mask);
4383 else if(cData->topic)
4384 return irccasecmp(new_topic, cData->topic);
4389 static CHANSERV_FUNC(cmd_topic)
4391 struct chanData *cData;
4394 cData = channel->channel_info;
4399 SetChannelTopic(channel, chanserv, cData->topic, 1);
4400 reply("CSMSG_TOPIC_SET", cData->topic);
4404 reply("CSMSG_NO_TOPIC", channel->name);
4408 topic = unsplit_string(argv + 1, argc - 1, NULL);
4409 /* If they say "!topic *", use an empty topic. */
4410 if((topic[0] == '*') && (topic[1] == 0))
4412 if(bad_topic(channel, user, topic))
4414 char *topic_mask = cData->topic_mask;
4417 char new_topic[TOPICLEN+1], tchar;
4418 int pos=0, starpos=-1, dpos=0, len;
4420 if(cData->flags & CHANNEL_ADVTOPIC)
4422 //first check if there is a leading 'modifier id'
4423 int advtopic_index = 0;
4426 for(; topic[pos]; pos++)
4428 if(topic[pos] == ' ')
4430 //leading number found, cut off and store value in advtopic_index
4432 advtopic_index = atoi(topic) - 1; //no zerobase
4433 topic = &topic[pos+1];
4434 /* If they say "!topic 2 *", unset advtopic id 2. */
4435 if((topic[0] == '*') && (topic[1] == 0))
4438 if(!isdigit(topic[pos]))
4441 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4444 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4447 if(cData->advtopic[advtopic_index])
4448 free(cData->advtopic[advtopic_index]);
4449 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4450 char *ptr = topic_mask;
4451 while(*ptr && (dpos <= TOPICLEN))
4457 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4458 numbuf[numpos++] = *ptr;
4461 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4463 new_topic[dpos++] = *ptr; //is % again
4466 advtopic_index--; //no zero base
4467 if(!cData->advtopic[advtopic_index])
4468 break; //just leave it empty
4469 len = strlen(cData->advtopic[advtopic_index]);
4470 if((dpos + len) > TOPICLEN)
4471 len = TOPICLEN + 1 - dpos;
4472 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4476 ptr++; /* and fall through */
4479 new_topic[dpos++] = *ptr;
4485 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4492 len = strlen(topic);
4493 if((dpos + len) > TOPICLEN)
4494 len = TOPICLEN + 1 - dpos;
4495 memcpy(new_topic+dpos, topic, len);
4499 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4500 default: new_topic[dpos++] = tchar; break;
4503 if((dpos > TOPICLEN) || tchar)
4506 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4507 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4511 new_topic[dpos] = 0;
4512 SetChannelTopic(channel, chanserv, new_topic, 1);
4514 reply("CSMSG_TOPIC_LOCKED", channel->name);
4519 SetChannelTopic(channel, chanserv, topic, 1);
4521 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4523 /* Grab the topic and save it as the default topic. */
4525 cData->topic = strdup(channel->topic);
4531 static CHANSERV_FUNC(cmd_mode)
4533 struct userData *uData;
4534 struct mod_chanmode *change;
4540 change = &channel->channel_info->modes;
4541 if(change->modes_set || change->modes_clear) {
4542 modcmd_chanmode_announce(change);
4543 reply("CSMSG_DEFAULTED_MODES", channel->name);
4545 reply("CSMSG_NO_MODES", channel->name);
4549 uData = GetChannelUser(channel->channel_info, user->handle_info);
4551 base_oplevel = MAXOPLEVEL;
4552 else if (uData->access >= UL_OWNER)
4555 base_oplevel = 1 + UL_OWNER - uData->access;
4556 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4559 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4563 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4564 && mode_lock_violated(&channel->channel_info->modes, change))
4567 mod_chanmode_format(&channel->channel_info->modes, modes);
4568 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4572 modcmd_chanmode_announce(change);
4573 mod_chanmode_format(change, fmt);
4574 mod_chanmode_free(change);
4575 reply("CSMSG_MODES_SET", fmt);
4580 chanserv_del_invite_mark(void *data)
4582 struct ChanUser *chanuser = data;
4583 struct chanNode *channel = chanuser->chan;
4585 if(!channel) return;
4586 for(i = 0; i < channel->invited.used; i++)
4588 if(channel->invited.list[i] == chanuser->user) {
4589 userList_remove(&channel->invited, chanuser->user);
4595 static CHANSERV_FUNC(cmd_invite)
4597 struct userNode *invite;
4598 struct ChanUser *chanuser;
4603 if(!(invite = GetUserH(argv[1])))
4605 reply("MSG_NICK_UNKNOWN", argv[1]);
4612 if(GetUserMode(channel, invite))
4614 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4618 for(i = 0; i < channel->invited.used; i++)
4620 if(channel->invited.list[i] == invite) {
4621 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4630 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4631 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4634 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4636 irc_invite(chanserv, invite, channel);
4638 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4640 userList_append(&channel->invited, invite);
4641 chanuser = calloc(1, sizeof(*chanuser));
4642 chanuser->user=invite;
4643 chanuser->chan=channel;
4644 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4649 static CHANSERV_FUNC(cmd_inviteme)
4651 if(GetUserMode(channel, user))
4653 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4656 if(channel->channel_info
4657 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4659 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4662 irc_invite(cmd->parent->bot, user, channel);
4666 static CHANSERV_FUNC(cmd_invitemeall)
4668 struct handle_info *target = user->handle_info;
4669 struct userData *uData;
4671 if(!target->channels)
4673 reply("CSMSG_SQUAT_ACCESS", target->handle);
4677 for(uData = target->channels; uData; uData = uData->u_next)
4679 struct chanData *cData = uData->channel;
4680 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4682 irc_invite(cmd->parent->bot, user, cData->channel);
4689 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4692 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4694 /* We display things based on two dimensions:
4695 * - Issue time: present or absent
4696 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4697 * (in order of precedence, so something both expired and revoked
4698 * only counts as revoked)
4700 combo = (suspended->issued ? 4 : 0)
4701 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4703 case 0: /* no issue time, indefinite expiration */
4704 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4706 case 1: /* no issue time, expires in future */
4707 intervalString(buf1, suspended->expires-now, user->handle_info);
4708 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4710 case 2: /* no issue time, expired */
4711 intervalString(buf1, now-suspended->expires, user->handle_info);
4712 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4714 case 3: /* no issue time, revoked */
4715 intervalString(buf1, now-suspended->revoked, user->handle_info);
4716 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4718 case 4: /* issue time set, indefinite expiration */
4719 intervalString(buf1, now-suspended->issued, user->handle_info);
4720 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4722 case 5: /* issue time set, expires in future */
4723 intervalString(buf1, now-suspended->issued, user->handle_info);
4724 intervalString(buf2, suspended->expires-now, user->handle_info);
4725 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4727 case 6: /* issue time set, expired */
4728 intervalString(buf1, now-suspended->issued, user->handle_info);
4729 intervalString(buf2, now-suspended->expires, user->handle_info);
4730 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4732 case 7: /* issue time set, revoked */
4733 intervalString(buf1, now-suspended->issued, user->handle_info);
4734 intervalString(buf2, now-suspended->revoked, user->handle_info);
4735 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4738 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4744 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4747 const char *fmt = "%a %b %d %H:%M %Y";
4748 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4750 if(giveownership->staff_issuer)
4752 if(giveownership->reason)
4753 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4754 giveownership->target, giveownership->target_access,
4755 giveownership->staff_issuer, buf, giveownership->reason);
4757 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4758 giveownership->target, giveownership->target_access,
4759 giveownership->staff_issuer, buf);
4763 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4767 static CHANSERV_FUNC(cmd_info)
4769 char modes[MAXLEN], buffer[INTERVALLEN];
4770 struct userData *uData, *owner;
4771 struct chanData *cData;
4772 struct do_not_register *dnr;
4777 cData = channel->channel_info;
4778 reply("CSMSG_CHANNEL_INFO", channel->name);
4780 uData = GetChannelUser(cData, user->handle_info);
4781 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4783 mod_chanmode_format(&cData->modes, modes);
4784 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4785 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4788 for(it = dict_first(cData->notes); it; it = iter_next(it))
4792 note = iter_data(it);
4793 if(!note_type_visible_to_user(cData, note->type, user))
4796 padding = PADLEN - 1 - strlen(iter_key(it));
4797 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4800 if(cData->max_time) {
4801 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4803 reply("CSMSG_CHANNEL_MAX", cData->max);
4805 for(owner = cData->users; owner; owner = owner->next)
4806 if(owner->access == UL_OWNER)
4807 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4808 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4809 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4810 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4812 privileged = IsStaff(user);
4814 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4815 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4816 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4818 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4819 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4821 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4823 struct suspended *suspended;
4824 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4825 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4826 show_suspension_info(cmd, user, suspended);
4828 else if(IsSuspended(cData))
4830 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4831 show_suspension_info(cmd, user, cData->suspended);
4834 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4836 struct giveownership *giveownership;
4837 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4838 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4839 show_giveownership_info(cmd, user, giveownership);
4844 static CHANSERV_FUNC(cmd_netinfo)
4846 extern unsigned long boot_time;
4847 extern unsigned long burst_length;
4848 char interval[INTERVALLEN];
4850 reply("CSMSG_NETWORK_INFO");
4851 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4852 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4853 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4854 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4855 reply("CSMSG_NETWORK_BANS", banCount);
4856 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4857 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4858 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4863 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4865 struct helpfile_table table;
4867 struct userNode *user;
4872 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4873 table.contents = alloca(list->used*sizeof(*table.contents));
4874 for(nn=0; nn<list->used; nn++)
4876 user = list->list[nn];
4877 if(user->modes & skip_flags)
4883 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4886 nick = alloca(strlen(user->nick)+3);
4887 sprintf(nick, "(%s)", user->nick);
4891 table.contents[table.length][0] = nick;
4894 table_send(chanserv, to->nick, 0, NULL, table);
4897 static CHANSERV_FUNC(cmd_ircops)
4899 reply("CSMSG_STAFF_OPERS");
4900 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4904 static CHANSERV_FUNC(cmd_helpers)
4906 reply("CSMSG_STAFF_HELPERS");
4907 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4911 static CHANSERV_FUNC(cmd_staff)
4913 reply("CSMSG_NETWORK_STAFF");
4914 cmd_ircops(CSFUNC_ARGS);
4915 cmd_helpers(CSFUNC_ARGS);
4919 static CHANSERV_FUNC(cmd_peek)
4921 struct modeNode *mn;
4922 char modes[MODELEN];
4924 struct helpfile_table table;
4925 int opcount = 0, voicecount = 0, srvcount = 0;
4927 irc_make_chanmode(channel, modes);
4929 reply("CSMSG_PEEK_INFO", channel->name);
4930 reply("CSMSG_PEEK_TOPIC", channel->topic);
4931 reply("CSMSG_PEEK_MODES", modes);
4935 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4936 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4937 for(n = 0; n < channel->members.used; n++)
4939 mn = channel->members.list[n];
4940 if(IsLocal(mn->user))
4942 else if(mn->modes & MODE_CHANOP)
4944 else if(mn->modes & MODE_VOICE)
4947 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4949 table.contents[table.length] = alloca(sizeof(**table.contents));
4950 table.contents[table.length][0] = mn->user->nick;
4954 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4955 (channel->members.used - opcount - voicecount - srvcount));
4959 reply("CSMSG_PEEK_OPS");
4960 table_send(chanserv, user->nick, 0, NULL, table);
4963 reply("CSMSG_PEEK_NO_OPS");
4967 static MODCMD_FUNC(cmd_wipeinfo)
4969 struct handle_info *victim;
4970 struct userData *ud, *actor, *real_actor;
4971 unsigned int override = 0;
4974 actor = GetChannelUser(channel->channel_info, user->handle_info);
4975 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4976 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4978 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4980 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4983 if((ud->access >= actor->access) && (ud != actor))
4985 reply("MSG_USER_OUTRANKED", victim->handle);
4988 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4989 override = CMD_LOG_OVERRIDE;
4993 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4994 return 1 | override;
4997 static CHANSERV_FUNC(cmd_resync)
4999 struct mod_chanmode *changes;
5000 struct chanData *cData = channel->channel_info;
5001 unsigned int ii, used;
5003 changes = mod_chanmode_alloc(channel->members.used * 2);
5004 for(ii = used = 0; ii < channel->members.used; ++ii)
5006 struct modeNode *mn = channel->members.list[ii];
5007 struct userData *uData;
5009 if(IsService(mn->user))
5012 uData = GetChannelAccess(cData, mn->user->handle_info);
5013 if(!cData->lvlOpts[lvlGiveOps]
5014 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
5016 if(!(mn->modes & MODE_CHANOP))
5018 if(!uData || IsUserAutoOp(uData))
5020 changes->args[used].mode = MODE_CHANOP;
5021 changes->args[used++].u.member = mn;
5022 if(!(mn->modes & MODE_VOICE))
5024 changes->args[used].mode = MODE_VOICE;
5025 changes->args[used++].u.member = mn;
5030 else if(!cData->lvlOpts[lvlGiveVoice]
5031 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5033 if(mn->modes & MODE_CHANOP)
5035 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5036 changes->args[used++].u.member = mn;
5038 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5040 changes->args[used].mode = MODE_VOICE;
5041 changes->args[used++].u.member = mn;
5048 changes->args[used].mode = MODE_REMOVE | mn->modes;
5049 changes->args[used++].u.member = mn;
5053 changes->argc = used;
5054 modcmd_chanmode_announce(changes);
5055 mod_chanmode_free(changes);
5056 reply("CSMSG_RESYNCED_USERS", channel->name);
5060 static CHANSERV_FUNC(cmd_seen)
5062 struct userData *uData;
5063 struct handle_info *handle;
5064 char seen[INTERVALLEN];
5068 if(!irccasecmp(argv[1], chanserv->nick))
5070 reply("CSMSG_IS_CHANSERV");
5074 if(!(handle = get_handle_info(argv[1])))
5076 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5080 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5082 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5087 reply("CSMSG_USER_PRESENT", handle->handle);
5088 else if(uData->seen)
5089 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5091 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5093 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5094 reply("CSMSG_USER_VACATION", handle->handle);
5099 static MODCMD_FUNC(cmd_names)
5101 struct userNode *targ;
5102 struct userData *targData;
5103 unsigned int ii, pos;
5106 for(ii=pos=0; ii<channel->members.used; ++ii)
5108 targ = channel->members.list[ii]->user;
5109 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5112 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5115 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5119 if(IsUserSuspended(targData))
5121 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5124 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5125 reply("CSMSG_END_NAMES", channel->name);
5130 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5132 switch(ntype->visible_type)
5134 case NOTE_VIS_ALL: return 1;
5135 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5136 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5141 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5143 struct userData *uData;
5145 switch(ntype->set_access_type)
5147 case NOTE_SET_CHANNEL_ACCESS:
5148 if(!user->handle_info)
5150 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5152 return uData->access >= ntype->set_access.min_ulevel;
5153 case NOTE_SET_CHANNEL_SETTER:
5154 return check_user_level(channel, user, lvlSetters, 1, 0);
5155 case NOTE_SET_PRIVILEGED: default:
5156 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5160 static CHANSERV_FUNC(cmd_note)
5162 struct chanData *cData;
5164 struct note_type *ntype;
5166 cData = channel->channel_info;
5169 reply("CSMSG_NOT_REGISTERED", channel->name);
5173 /* If no arguments, show all visible notes for the channel. */
5179 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5181 note = iter_data(it);
5182 if(!note_type_visible_to_user(cData, note->type, user))
5185 reply("CSMSG_NOTELIST_HEADER", channel->name);
5186 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5189 reply("CSMSG_NOTELIST_END", channel->name);
5191 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5193 /* If one argument, show the named note. */
5196 if((note = dict_find(cData->notes, argv[1], NULL))
5197 && note_type_visible_to_user(cData, note->type, user))
5199 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5201 else if((ntype = dict_find(note_types, argv[1], NULL))
5202 && note_type_visible_to_user(NULL, ntype, user))
5204 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5209 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5213 /* Assume they're trying to set a note. */
5217 ntype = dict_find(note_types, argv[1], NULL);
5220 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5223 else if(note_type_settable_by_user(channel, ntype, user))
5225 note_text = unsplit_string(argv+2, argc-2, NULL);
5226 if((note = dict_find(cData->notes, argv[1], NULL)))
5227 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5228 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5229 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5231 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5233 /* The note is viewable to staff only, so return 0
5234 to keep the invocation from getting logged (or
5235 regular users can see it in !events). */
5241 reply("CSMSG_NO_ACCESS");
5248 static CHANSERV_FUNC(cmd_delnote)
5253 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5254 || !note_type_settable_by_user(channel, note->type, user))
5256 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5259 dict_remove(channel->channel_info->notes, note->type->name);
5260 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5264 static CHANSERV_FUNC(cmd_events)
5266 struct logSearch discrim;
5267 struct logReport report;
5268 unsigned int matches, limit;
5270 limit = (argc > 1) ? atoi(argv[1]) : 10;
5271 if(limit < 1 || limit > 200)
5274 memset(&discrim, 0, sizeof(discrim));
5275 discrim.masks.bot = chanserv;
5276 discrim.masks.channel_name = channel->name;
5278 discrim.masks.command = argv[2];
5279 discrim.limit = limit;
5280 discrim.max_time = INT_MAX;
5281 discrim.severities = 1 << LOG_COMMAND;
5282 report.reporter = chanserv;
5284 reply("CSMSG_EVENT_SEARCH_RESULTS");
5285 matches = log_entry_search(&discrim, log_report_entry, &report);
5287 reply("MSG_MATCH_COUNT", matches);
5289 reply("MSG_NO_MATCHES");
5293 static CHANSERV_FUNC(cmd_say)
5299 msg = unsplit_string(argv + 1, argc - 1, NULL);
5300 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5302 else if(*argv[1] == '*' && argv[1][1] != '\0')
5304 struct handle_info *hi;
5305 struct userNode *authed;
5308 msg = unsplit_string(argv + 2, argc - 2, NULL);
5310 if (!(hi = get_handle_info(argv[1] + 1)))
5312 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5316 for (authed = hi->users; authed; authed = authed->next_authed)
5317 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5319 else if(GetUserH(argv[1]))
5322 msg = unsplit_string(argv + 2, argc - 2, NULL);
5323 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5327 reply("MSG_NOT_TARGET_NAME");
5333 static CHANSERV_FUNC(cmd_emote)
5339 /* CTCP is so annoying. */
5340 msg = unsplit_string(argv + 1, argc - 1, NULL);
5341 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5343 else if(*argv[1] == '*' && argv[1][1] != '\0')
5345 struct handle_info *hi;
5346 struct userNode *authed;
5349 msg = unsplit_string(argv + 2, argc - 2, NULL);
5351 if (!(hi = get_handle_info(argv[1] + 1)))
5353 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5357 for (authed = hi->users; authed; authed = authed->next_authed)
5358 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5360 else if(GetUserH(argv[1]))
5362 msg = unsplit_string(argv + 2, argc - 2, NULL);
5363 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5367 reply("MSG_NOT_TARGET_NAME");
5373 struct channelList *
5374 chanserv_support_channels(void)
5376 return &chanserv_conf.support_channels;
5379 static CHANSERV_FUNC(cmd_expire)
5381 int channel_count = registered_channels;
5382 expire_channels(chanserv);
5383 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5388 chanserv_expire_suspension(void *data)
5390 struct suspended *suspended = data;
5391 struct chanNode *channel;
5394 /* Update the channel registration data structure. */
5395 if(!suspended->expires || (now < suspended->expires))
5396 suspended->revoked = now;
5397 channel = suspended->cData->channel;
5398 suspended->cData->channel = channel;
5399 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5401 /* If appropriate, re-join ChanServ to the channel. */
5402 if(!IsOffChannel(suspended->cData))
5404 spamserv_cs_suspend(channel, 0, 0, NULL);
5405 ss_cs_join_channel(channel, 1);
5408 /* Mark everyone currently in the channel as present. */
5409 for(ii = 0; ii < channel->members.used; ++ii)
5411 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5420 static CHANSERV_FUNC(cmd_csuspend)
5422 struct suspended *suspended;
5423 char reason[MAXLEN];
5424 unsigned long expiry, duration;
5425 struct userData *uData;
5429 if(IsProtected(channel->channel_info))
5431 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5435 if(argv[1][0] == '!')
5437 else if(IsSuspended(channel->channel_info))
5439 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5440 show_suspension_info(cmd, user, channel->channel_info->suspended);
5444 if(!strcmp(argv[1], "0"))
5446 else if((duration = ParseInterval(argv[1])))
5447 expiry = now + duration;
5450 reply("MSG_INVALID_DURATION", argv[1]);
5454 unsplit_string(argv + 2, argc - 2, reason);
5456 suspended = calloc(1, sizeof(*suspended));
5457 suspended->revoked = 0;
5458 suspended->issued = now;
5459 suspended->suspender = strdup(user->handle_info->handle);
5460 suspended->expires = expiry;
5461 suspended->reason = strdup(reason);
5462 suspended->cData = channel->channel_info;
5463 suspended->previous = suspended->cData->suspended;
5464 suspended->cData->suspended = suspended;
5466 if(suspended->expires)
5467 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5469 if(IsSuspended(channel->channel_info))
5471 suspended->previous->revoked = now;
5472 if(suspended->previous->expires)
5473 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5474 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5475 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5479 /* Mark all users in channel as absent. */
5480 for(uData = channel->channel_info->users; uData; uData = uData->next)
5489 /* Mark the channel as suspended, then part. */
5490 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5491 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5492 DelChannelUser(chanserv, channel, suspended->reason, 0);
5493 reply("CSMSG_SUSPENDED", channel->name);
5494 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5495 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5500 static CHANSERV_FUNC(cmd_cunsuspend)
5502 struct suspended *suspended;
5503 char message[MAXLEN];
5505 if(!IsSuspended(channel->channel_info))
5507 reply("CSMSG_NOT_SUSPENDED", channel->name);
5511 suspended = channel->channel_info->suspended;
5513 /* Expire the suspension and join ChanServ to the channel. */
5514 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5515 chanserv_expire_suspension(suspended);
5516 reply("CSMSG_UNSUSPENDED", channel->name);
5517 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5518 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5522 typedef struct chanservSearch
5527 unsigned long unvisited;
5528 unsigned long registered;
5530 unsigned long flags;
5534 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5537 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5542 search = malloc(sizeof(struct chanservSearch));
5543 memset(search, 0, sizeof(*search));
5546 for(i = 0; i < argc; i++)
5548 /* Assume all criteria require arguments. */
5551 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5555 if(!irccasecmp(argv[i], "name"))
5556 search->name = argv[++i];
5557 else if(!irccasecmp(argv[i], "registrar"))
5558 search->registrar = argv[++i];
5559 else if(!irccasecmp(argv[i], "unvisited"))
5560 search->unvisited = ParseInterval(argv[++i]);
5561 else if(!irccasecmp(argv[i], "registered"))
5562 search->registered = ParseInterval(argv[++i]);
5563 else if(!irccasecmp(argv[i], "flags"))
5566 if(!irccasecmp(argv[i], "nodelete"))
5567 search->flags |= CHANNEL_NODELETE;
5568 else if(!irccasecmp(argv[i], "suspended"))
5569 search->flags |= CHANNEL_SUSPENDED;
5570 else if(!irccasecmp(argv[i], "unreviewed"))
5571 search->flags |= CHANNEL_UNREVIEWED;
5574 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5578 else if(!irccasecmp(argv[i], "limit"))
5579 search->limit = strtoul(argv[++i], NULL, 10);
5582 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5587 if(search->name && !strcmp(search->name, "*"))
5589 if(search->registrar && !strcmp(search->registrar, "*"))
5590 search->registrar = 0;
5599 chanserv_channel_match(struct chanData *channel, search_t search)
5601 const char *name = channel->channel->name;
5602 if((search->name && !match_ircglob(name, search->name)) ||
5603 (search->registrar && !channel->registrar) ||
5604 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5605 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5606 (search->registered && (now - channel->registered) > search->registered) ||
5607 (search->flags && ((search->flags & channel->flags) != search->flags)))
5614 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5616 struct chanData *channel;
5617 unsigned int matches = 0;
5619 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5621 if(!chanserv_channel_match(channel, search))
5631 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5636 search_print(struct chanData *channel, void *data)
5638 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5641 static CHANSERV_FUNC(cmd_search)
5644 unsigned int matches;
5645 channel_search_func action;
5649 if(!irccasecmp(argv[1], "count"))
5650 action = search_count;
5651 else if(!irccasecmp(argv[1], "print"))
5652 action = search_print;
5655 reply("CSMSG_ACTION_INVALID", argv[1]);
5659 search = chanserv_search_create(user, argc - 2, argv + 2);
5663 if(action == search_count)
5664 search->limit = INT_MAX;
5666 if(action == search_print)
5667 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5669 matches = chanserv_channel_search(search, action, user);
5672 reply("MSG_MATCH_COUNT", matches);
5674 reply("MSG_NO_MATCHES");
5680 static CHANSERV_FUNC(cmd_unvisited)
5682 struct chanData *cData;
5683 unsigned long interval = chanserv_conf.channel_expire_delay;
5684 char buffer[INTERVALLEN];
5685 unsigned int limit = 25, matches = 0;
5689 interval = ParseInterval(argv[1]);
5691 limit = atoi(argv[2]);
5694 intervalString(buffer, interval, user->handle_info);
5695 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5697 for(cData = channelList; cData && matches < limit; cData = cData->next)
5699 if((now - cData->visited) < interval)
5702 intervalString(buffer, now - cData->visited, user->handle_info);
5703 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5710 static MODCMD_FUNC(chan_opt_defaulttopic)
5716 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5718 reply("CSMSG_TOPIC_LOCKED", channel->name);
5722 topic = unsplit_string(argv+1, argc-1, NULL);
5724 free(channel->channel_info->topic);
5725 if(topic[0] == '*' && topic[1] == 0)
5727 topic = channel->channel_info->topic = NULL;
5731 topic = channel->channel_info->topic = strdup(topic);
5732 if(channel->channel_info->topic_mask
5733 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5734 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5736 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5739 if(channel->channel_info->topic)
5740 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5742 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5746 static MODCMD_FUNC(chan_opt_topicmask)
5750 struct chanData *cData = channel->channel_info;
5753 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5755 reply("CSMSG_TOPIC_LOCKED", channel->name);
5759 mask = unsplit_string(argv+1, argc-1, NULL);
5761 if(cData->topic_mask)
5762 free(cData->topic_mask);
5763 if(mask[0] == '*' && mask[1] == 0)
5765 cData->topic_mask = 0;
5769 cData->topic_mask = strdup(mask);
5771 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5772 else if(!match_ircglob(cData->topic, cData->topic_mask))
5773 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5777 if(channel->channel_info->topic_mask)
5778 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5780 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5784 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5788 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5792 if(greeting[0] == '*' && greeting[1] == 0)
5796 unsigned int length = strlen(greeting);
5797 if(length > chanserv_conf.greeting_length)
5799 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5802 *data = strdup(greeting);
5811 reply(name, user_find_message(user, "MSG_NONE"));
5815 static MODCMD_FUNC(chan_opt_greeting)
5817 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5820 static MODCMD_FUNC(chan_opt_usergreeting)
5822 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5825 static MODCMD_FUNC(chan_opt_modes)
5827 struct mod_chanmode *new_modes;
5832 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5834 reply("CSMSG_NO_ACCESS");
5837 if(argv[1][0] == '*' && argv[1][1] == 0)
5839 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5841 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)))
5843 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5846 else if(new_modes->argc > 1)
5848 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5849 mod_chanmode_free(new_modes);
5854 channel->channel_info->modes = *new_modes;
5855 modcmd_chanmode_announce(new_modes);
5856 mod_chanmode_free(new_modes);
5860 mod_chanmode_format(&channel->channel_info->modes, modes);
5862 reply("CSMSG_SET_MODES", modes);
5864 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5868 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5870 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5872 struct chanData *cData = channel->channel_info;
5877 /* Set flag according to value. */
5878 if(enabled_string(argv[1]))
5880 cData->flags |= mask;
5883 else if(disabled_string(argv[1]))
5885 cData->flags &= ~mask;
5890 reply("MSG_INVALID_BINARY", argv[1]);
5896 /* Find current option value. */
5897 value = (cData->flags & mask) ? 1 : 0;
5901 reply(name, user_find_message(user, "MSG_ON"));
5903 reply(name, user_find_message(user, "MSG_OFF"));
5907 static MODCMD_FUNC(chan_opt_nodelete)
5909 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5911 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5915 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5918 static MODCMD_FUNC(chan_opt_dynlimit)
5920 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5923 static MODCMD_FUNC(chan_opt_advtopic)
5925 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5928 static MODCMD_FUNC(chan_opt_offchannel)
5930 struct chanData *cData = channel->channel_info;
5935 /* Set flag according to value. */
5936 if(enabled_string(argv[1]))
5938 if(!IsOffChannel(cData))
5939 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5940 cData->flags |= CHANNEL_OFFCHANNEL;
5943 else if(disabled_string(argv[1]))
5945 if(IsOffChannel(cData))
5947 struct mod_chanmode change;
5948 mod_chanmode_init(&change);
5950 change.args[0].mode = MODE_CHANOP;
5951 change.args[0].u.member = AddChannelUser(chanserv, channel);
5952 mod_chanmode_announce(chanserv, channel, &change);
5954 cData->flags &= ~CHANNEL_OFFCHANNEL;
5959 reply("MSG_INVALID_BINARY", argv[1]);
5965 /* Find current option value. */
5966 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5970 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5972 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5976 static MODCMD_FUNC(chan_opt_unreviewed)
5978 struct chanData *cData = channel->channel_info;
5979 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5985 /* The two directions can have different ACLs. */
5986 if(enabled_string(argv[1]))
5988 else if(disabled_string(argv[1]))
5992 reply("MSG_INVALID_BINARY", argv[1]);
5996 if (new_value != value)
5998 struct svccmd *subcmd;
5999 char subcmd_name[32];
6001 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
6002 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
6005 reply("MSG_COMMAND_DISABLED", subcmd_name);
6008 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6012 cData->flags |= CHANNEL_UNREVIEWED;
6015 free(cData->registrar);
6016 cData->registrar = strdup(user->handle_info->handle);
6017 cData->flags &= ~CHANNEL_UNREVIEWED;
6024 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6026 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6030 static MODCMD_FUNC(chan_opt_defaults)
6032 struct userData *uData;
6033 struct chanData *cData;
6034 const char *confirm;
6035 enum levelOption lvlOpt;
6036 enum charOption chOpt;
6038 cData = channel->channel_info;
6039 uData = GetChannelUser(cData, user->handle_info);
6040 if(!uData || (uData->access < UL_OWNER))
6042 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6045 confirm = make_confirmation_string(uData);
6046 if((argc < 2) || strcmp(argv[1], confirm))
6048 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6051 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6052 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6053 cData->modes = chanserv_conf.default_modes;
6054 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6055 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6056 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6057 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6058 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6063 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6065 struct chanData *cData = channel->channel_info;
6066 struct userData *uData;
6067 unsigned short value;
6071 if(!check_user_level(channel, user, option, 1, 1))
6073 reply("CSMSG_CANNOT_SET");
6076 value = user_level_from_name(argv[1], UL_OWNER+1);
6077 if(!value && strcmp(argv[1], "0"))
6079 reply("CSMSG_INVALID_ACCESS", argv[1]);
6082 uData = GetChannelUser(cData, user->handle_info);
6083 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6085 reply("CSMSG_BAD_SETLEVEL");
6091 if(value > cData->lvlOpts[lvlGiveOps])
6093 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6098 if(value < cData->lvlOpts[lvlGiveVoice])
6100 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6105 /* This test only applies to owners, since non-owners
6106 * trying to set an option to above their level get caught
6107 * by the CSMSG_BAD_SETLEVEL test above.
6109 if(value > uData->access)
6111 reply("CSMSG_BAD_SETTERS");
6118 cData->lvlOpts[option] = value;
6120 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6124 static MODCMD_FUNC(chan_opt_enfops)
6126 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6129 static MODCMD_FUNC(chan_opt_giveops)
6131 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6134 static MODCMD_FUNC(chan_opt_enfmodes)
6136 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6139 static MODCMD_FUNC(chan_opt_enftopic)
6141 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6144 static MODCMD_FUNC(chan_opt_pubcmd)
6146 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6149 static MODCMD_FUNC(chan_opt_setters)
6151 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6154 static MODCMD_FUNC(chan_opt_ctcpusers)
6156 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6159 static MODCMD_FUNC(chan_opt_userinfo)
6161 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6164 static MODCMD_FUNC(chan_opt_givevoice)
6166 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6169 static MODCMD_FUNC(chan_opt_topicsnarf)
6171 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6174 static MODCMD_FUNC(chan_opt_vote)
6176 return channel_level_option(lvlVote, CSFUNC_ARGS);
6179 static MODCMD_FUNC(chan_opt_inviteme)
6181 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6185 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6187 struct chanData *cData = channel->channel_info;
6188 int count = charOptions[option].count, idx;
6192 idx = atoi(argv[1]);
6194 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6196 reply("CSMSG_INVALID_NUMERIC", idx);
6197 /* Show possible values. */
6198 for(idx = 0; idx < count; idx++)
6199 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6203 cData->chOpts[option] = charOptions[option].values[idx].value;
6207 /* Find current option value. */
6210 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6214 /* Somehow, the option value is corrupt; reset it to the default. */
6215 cData->chOpts[option] = charOptions[option].default_value;
6220 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6224 static MODCMD_FUNC(chan_opt_protect)
6226 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6229 static MODCMD_FUNC(chan_opt_toys)
6231 return channel_multiple_option(chToys, CSFUNC_ARGS);
6234 static MODCMD_FUNC(chan_opt_ctcpreaction)
6236 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6239 static MODCMD_FUNC(chan_opt_topicrefresh)
6241 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6244 static struct svccmd_list set_shows_list;
6247 handle_svccmd_unbind(struct svccmd *target) {
6249 for(ii=0; ii<set_shows_list.used; ++ii)
6250 if(target == set_shows_list.list[ii])
6251 set_shows_list.used = 0;
6254 static CHANSERV_FUNC(cmd_set)
6256 struct svccmd *subcmd;
6260 /* Check if we need to (re-)initialize set_shows_list. */
6261 if(!set_shows_list.used)
6263 if(!set_shows_list.size)
6265 set_shows_list.size = chanserv_conf.set_shows->used;
6266 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6268 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6270 const char *name = chanserv_conf.set_shows->list[ii];
6271 sprintf(buf, "%s %s", argv[0], name);
6272 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6275 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6278 svccmd_list_append(&set_shows_list, subcmd);
6284 reply("CSMSG_CHANNEL_OPTIONS");
6285 for(ii = 0; ii < set_shows_list.used; ii++)
6287 subcmd = set_shows_list.list[ii];
6288 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6293 sprintf(buf, "%s %s", argv[0], argv[1]);
6294 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6297 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6300 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6302 reply("CSMSG_NO_ACCESS");
6308 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6312 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6314 struct userData *uData;
6316 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6319 reply("CSMSG_NOT_USER", channel->name);
6325 /* Just show current option value. */
6327 else if(enabled_string(argv[1]))
6329 uData->flags |= mask;
6331 else if(disabled_string(argv[1]))
6333 uData->flags &= ~mask;
6337 reply("MSG_INVALID_BINARY", argv[1]);
6341 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6345 static MODCMD_FUNC(user_opt_noautoop)
6347 struct userData *uData;
6349 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6352 reply("CSMSG_NOT_USER", channel->name);
6355 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6356 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6358 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6361 static MODCMD_FUNC(user_opt_autoinvite)
6363 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6365 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6367 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6370 static MODCMD_FUNC(user_opt_info)
6372 struct userData *uData;
6375 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6379 /* If they got past the command restrictions (which require access)
6380 * but fail this test, we have some fool with security override on.
6382 reply("CSMSG_NOT_USER", channel->name);
6389 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6390 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6392 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6395 bp = strcspn(infoline, "\001");
6398 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6403 if(infoline[0] == '*' && infoline[1] == 0)
6406 uData->info = strdup(infoline);
6409 reply("CSMSG_USET_INFO", uData->info);
6411 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6415 struct svccmd_list uset_shows_list;
6417 static CHANSERV_FUNC(cmd_uset)
6419 struct svccmd *subcmd;
6423 /* Check if we need to (re-)initialize uset_shows_list. */
6424 if(!uset_shows_list.used)
6428 "NoAutoOp", "AutoInvite", "Info"
6431 if(!uset_shows_list.size)
6433 uset_shows_list.size = ArrayLength(options);
6434 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6436 for(ii = 0; ii < ArrayLength(options); ii++)
6438 const char *name = options[ii];
6439 sprintf(buf, "%s %s", argv[0], name);
6440 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6443 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6446 svccmd_list_append(&uset_shows_list, subcmd);
6452 /* Do this so options are presented in a consistent order. */
6453 reply("CSMSG_USER_OPTIONS");
6454 for(ii = 0; ii < uset_shows_list.used; ii++)
6455 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6459 sprintf(buf, "%s %s", argv[0], argv[1]);
6460 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6463 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6467 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6470 static CHANSERV_FUNC(cmd_giveownership)
6472 struct handle_info *new_owner_hi;
6473 struct userData *new_owner;
6474 struct userData *curr_user;
6475 struct userData *invoker;
6476 struct chanData *cData = channel->channel_info;
6477 struct do_not_register *dnr;
6478 const char *confirm;
6479 struct giveownership *giveownership;
6480 unsigned int force, override;
6481 unsigned short co_access, new_owner_old_access;
6482 char reason[MAXLEN], transfer_reason[MAXLEN];
6485 curr_user = GetChannelAccess(cData, user->handle_info);
6486 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6488 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6489 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6490 && (uData->access > 500)
6491 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6492 || uData->access < 500));
6494 if(!curr_user || (curr_user->access != UL_OWNER))
6496 struct userData *owner = NULL;
6497 for(curr_user = channel->channel_info->users;
6499 curr_user = curr_user->next)
6501 if(curr_user->access != UL_OWNER)
6505 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6512 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6514 char delay[INTERVALLEN];
6515 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6516 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6519 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6521 if(new_owner_hi == user->handle_info)
6523 reply("CSMSG_NO_TRANSFER_SELF");
6526 new_owner = GetChannelAccess(cData, new_owner_hi);
6531 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6535 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6539 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6541 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6544 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6545 if(!IsHelping(user))
6546 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6548 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6551 invoker = GetChannelUser(cData, user->handle_info);
6552 if(invoker->access <= UL_OWNER)
6554 confirm = make_confirmation_string(curr_user);
6555 if((argc < 3) || strcmp(argv[2], confirm))
6557 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6561 new_owner_old_access = new_owner->access;
6562 if(new_owner->access >= UL_COOWNER)
6563 co_access = new_owner->access;
6565 co_access = UL_COOWNER;
6566 new_owner->access = UL_OWNER;
6568 curr_user->access = co_access;
6569 cData->ownerTransfer = now;
6570 giveownership = calloc(1, sizeof(*giveownership));
6571 giveownership->issued = now;
6572 giveownership->old_owner = curr_user->handle->handle;
6573 giveownership->target = new_owner_hi->handle;
6574 giveownership->target_access = new_owner_old_access;
6577 if(argc > (2 + force))
6579 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6580 giveownership->reason = strdup(transfer_reason);
6582 giveownership->staff_issuer = strdup(user->handle_info->handle);
6585 giveownership->previous = channel->channel_info->giveownership;
6586 channel->channel_info->giveownership = giveownership;
6587 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6588 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6589 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6594 chanserv_expire_user_suspension(void *data)
6596 struct userData *target = data;
6598 target->expires = 0;
6599 target->flags &= ~USER_SUSPENDED;
6602 static CHANSERV_FUNC(cmd_suspend)
6604 struct handle_info *hi;
6605 struct userData *actor, *real_actor, *target;
6606 unsigned int override = 0;
6610 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6611 actor = GetChannelUser(channel->channel_info, user->handle_info);
6612 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6613 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6615 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6618 if(target->access >= actor->access)
6620 reply("MSG_USER_OUTRANKED", hi->handle);
6623 if(target->flags & USER_SUSPENDED)
6625 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6630 target->present = 0;
6633 if(!real_actor || target->access >= real_actor->access)
6634 override = CMD_LOG_OVERRIDE;
6635 if(!strcmp(argv[2], "0"))
6639 unsigned int duration;
6640 if(!(duration = ParseInterval(argv[2])))
6642 reply("MSG_INVALID_DURATION", argv[2]);
6645 expiry = now + duration;
6648 target->expires = expiry;
6651 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6653 target->flags |= USER_SUSPENDED;
6654 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6655 return 1 | override;
6658 static CHANSERV_FUNC(cmd_unsuspend)
6660 struct handle_info *hi;
6661 struct userData *actor, *real_actor, *target;
6662 unsigned int override = 0;
6665 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6666 actor = GetChannelUser(channel->channel_info, user->handle_info);
6667 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6668 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6670 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6673 if(target->access >= actor->access)
6675 reply("MSG_USER_OUTRANKED", hi->handle);
6678 if(!(target->flags & USER_SUSPENDED))
6680 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6683 if(!real_actor || target->access >= real_actor->access)
6684 override = CMD_LOG_OVERRIDE;
6685 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
6686 target->flags &= ~USER_SUSPENDED;
6687 scan_user_presence(target, NULL);
6688 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6689 return 1 | override;
6692 static MODCMD_FUNC(cmd_deleteme)
6694 struct handle_info *hi;
6695 struct userData *target;
6696 const char *confirm_string;
6697 unsigned short access_level;
6700 hi = user->handle_info;
6701 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6703 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6706 if(target->access == UL_OWNER)
6708 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6711 confirm_string = make_confirmation_string(target);
6712 if((argc < 2) || strcmp(argv[1], confirm_string))
6714 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6717 access_level = target->access;
6718 channel_name = strdup(channel->name);
6719 del_channel_user(target, 1);
6720 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6725 static CHANSERV_FUNC(cmd_addvote)
6727 struct chanData *cData = channel->channel_info;
6728 struct userData *uData, *target;
6729 struct handle_info *hi;
6730 if (!cData) return 0;
6732 hi = user->handle_info;
6733 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6735 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6738 if(target->access < 300) {
6739 reply("CSMSG_NO_ACCESS");
6743 reply("CSMSG_ADDVOTE_FULL");
6747 msg = unsplit_string(argv + 1, argc - 1, NULL);
6748 cData->vote = strdup(msg);
6749 cData->vote_start=0;
6750 dict_delete(cData->vote_options);
6751 cData->vote_options = dict_new();
6752 dict_set_free_data(cData->vote_options, free_vote_options);
6753 for(uData = channel->channel_info->users; uData; uData = uData->next)
6758 reply("CSMSG_ADDVOTE_DONE");
6762 static CHANSERV_FUNC(cmd_delvote)
6764 struct chanData *cData = channel->channel_info;
6765 struct userData *target;
6766 struct handle_info *hi;
6767 if (!cData) return 0;
6768 hi = user->handle_info;
6769 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6771 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6774 if(target->access < 300) {
6775 reply("CSMSG_NO_ACCESS");
6779 reply("CSMSG_NO_VOTE");
6784 reply("CSMSG_DELVOTE_DONE");
6788 static CHANSERV_FUNC(cmd_addoption)
6790 struct chanData *cData = channel->channel_info;
6791 struct userData *target;
6792 struct handle_info *hi;
6793 if (!cData) return 0;
6795 hi = user->handle_info;
6796 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6798 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6801 if(target->access < 300) {
6802 reply("CSMSG_NO_ACCESS");
6806 reply("CSMSG_NO_VOTE");
6812 msg = unsplit_string(argv + 1, argc - 1, NULL);
6815 unsigned int lastid = 1;
6816 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6817 struct vote_option *cvOpt = iter_data(it);
6818 if(cvOpt->option_id > lastid)
6819 lastid = cvOpt->option_id;
6821 struct vote_option *vOpt;
6822 vOpt = calloc(1, sizeof(*vOpt));
6823 vOpt->name = strdup(msg);
6824 vOpt->option_id = (lastid + 1);
6826 sprintf(str,"%i",(lastid + 1));
6827 vOpt->option_str = strdup(str);
6829 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6831 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6835 static CHANSERV_FUNC(cmd_deloption)
6837 struct chanData *cData = channel->channel_info;
6838 struct userData *uData, *target;
6839 struct handle_info *hi;
6840 if (!cData) return 0;
6842 hi = user->handle_info;
6843 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6845 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6848 if(target->access < 300) {
6849 reply("CSMSG_NO_ACCESS");
6853 reply("CSMSG_NO_VOTE");
6856 if(cData->vote_start) {
6857 if(dict_size(cData->vote_options) < 3) {
6858 reply("CSMSG_VOTE_NEED_OPTIONS");
6863 int find_id = atoi(argv[1]);
6865 unsigned int found = 0;
6868 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6870 if (find_id == ii) {
6871 struct vote_option *vOpt = iter_data(it);
6872 found = vOpt->option_id;
6874 sprintf(str,"%i",vOpt->option_id);
6875 dict_remove(cData->vote_options, str);
6880 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6881 if(uData->votefor == found) {
6886 reply("CSMSG_DELOPTION_DONE");
6889 reply("CSMSG_DELOPTION_NONE");
6894 static CHANSERV_FUNC(cmd_vote)
6896 struct chanData *cData = channel->channel_info;
6897 struct userData *target;
6898 struct handle_info *hi;
6899 unsigned int votedfor = 0;
6900 char *votedfor_str = NULL;
6902 if (!cData || !cData->vote) {
6903 reply("CSMSG_NO_VOTE");
6906 if(argc > 1 && cData->vote_start) {
6907 hi = user->handle_info;
6908 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6910 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6913 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6914 reply("CSMSG_NO_ACCESS");
6918 reply("CSMSG_VOTE_VOTED");
6921 int find_id = atoi(argv[1]);
6924 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6926 if (find_id == ii) {
6927 struct vote_option *vOpt = iter_data(it);
6930 target->votefor = vOpt->option_id;
6931 votedfor = vOpt->option_id;
6932 votedfor_str = vOpt->name;
6936 reply("CSMSG_VOTE_INVALID");
6940 if (!cData->vote_start) {
6941 reply("CSMSG_VOTE_NOT_STARTED");
6943 reply("CSMSG_VOTE_QUESTION",cData->vote);
6945 unsigned int voteid = 0;
6948 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6949 struct vote_option *vOpt = iter_data(it);
6951 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6953 if(argc > 1 && cData->vote_start && votedfor_str) {
6954 reply("CSMSG_VOTE_DONE",votedfor_str);
6959 static CHANSERV_FUNC(cmd_startvote)
6961 struct chanData *cData = channel->channel_info;
6962 struct userData *target;
6963 struct handle_info *hi;
6964 if (!cData) return 0;
6965 hi = user->handle_info;
6966 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6968 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6971 if(target->access < 300) {
6972 reply("CSMSG_NO_ACCESS");
6976 reply("CSMSG_NO_VOTE");
6979 if(cData->vote_start) {
6980 reply("CSMSG_STARTVOTE_RUNNING");
6983 if(dict_size(cData->vote_options) < 2) {
6984 reply("CSMSG_VOTE_NEED_OPTIONS");
6987 cData->vote_start = 1;
6988 char response[MAXLEN];
6989 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6990 irc_privmsg(cmd->parent->bot, channel->name, response);
6991 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6992 irc_privmsg(cmd->parent->bot, channel->name, response);
6993 unsigned int voteid = 0;
6995 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6996 struct vote_option *vOpt = iter_data(it);
6998 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6999 irc_privmsg(cmd->parent->bot, channel->name, response);
7001 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
7002 irc_privmsg(cmd->parent->bot, channel->name, response);
7003 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
7004 irc_privmsg(cmd->parent->bot, channel->name, response);
7008 static CHANSERV_FUNC(cmd_endvote)
7010 struct chanData *cData = channel->channel_info;
7011 struct userData *target;
7012 struct handle_info *hi;
7013 if (!cData) return 0;
7014 hi = user->handle_info;
7015 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7017 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7020 if(target->access < 300) {
7021 reply("CSMSG_NO_ACCESS");
7025 reply("CSMSG_NO_VOTE");
7028 if(!cData->vote_start) {
7029 reply("CSMSG_ENDVOTE_STOPPED");
7032 cData->vote_start = 0;
7033 reply("CSMSG_ENDVOTE_DONE");
7037 static CHANSERV_FUNC(cmd_voteresults)
7039 struct chanData *cData = channel->channel_info;
7040 struct userData *target;
7041 struct handle_info *hi;
7042 if (!cData) return 0;
7044 reply("CSMSG_NO_VOTE");
7047 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7048 hi = user->handle_info;
7049 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7051 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7054 if(target->access < 300) {
7055 reply("CSMSG_NO_ACCESS");
7058 char response[MAXLEN];
7059 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7060 irc_privmsg(cmd->parent->bot, channel->name, response);
7061 unsigned int voteid = 0;
7063 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7064 struct vote_option *vOpt = iter_data(it);
7066 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7067 irc_privmsg(cmd->parent->bot, channel->name, response);
7070 reply("CSMSG_VOTE_QUESTION",cData->vote);
7071 unsigned int voteid = 0;
7073 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7074 struct vote_option *vOpt = iter_data(it);
7076 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7083 chanserv_refresh_topics(UNUSED_ARG(void *data))
7085 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7086 struct chanData *cData;
7089 for(cData = channelList; cData; cData = cData->next)
7091 if(IsSuspended(cData))
7093 opt = cData->chOpts[chTopicRefresh];
7096 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7099 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7100 cData->last_refresh = refresh_num;
7102 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7105 static CHANSERV_FUNC(cmd_unf)
7109 char response[MAXLEN];
7110 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7111 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7112 irc_privmsg(cmd->parent->bot, channel->name, response);
7115 reply("CSMSG_UNF_RESPONSE");
7119 static CHANSERV_FUNC(cmd_ping)
7123 char response[MAXLEN];
7124 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7125 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7126 irc_privmsg(cmd->parent->bot, channel->name, response);
7129 reply("CSMSG_PING_RESPONSE");
7133 static CHANSERV_FUNC(cmd_pong)
7137 char response[MAXLEN];
7138 const char *fmt = user_find_message(user, "CSMSG_PONG_RESPONSE");
7139 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7140 irc_privmsg(cmd->parent->bot, channel->name, response);
7143 reply("CSMSG_PONG_RESPONSE");
7147 static CHANSERV_FUNC(cmd_wut)
7151 char response[MAXLEN];
7152 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7153 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7154 irc_privmsg(cmd->parent->bot, channel->name, response);
7157 reply("CSMSG_WUT_RESPONSE");
7161 static CHANSERV_FUNC(cmd_8ball)
7163 unsigned int i, j, accum;
7168 for(i=1; i<argc; i++)
7169 for(j=0; argv[i][j]; j++)
7170 accum = (accum << 5) - accum + toupper(argv[i][j]);
7171 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7174 char response[MAXLEN];
7175 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7176 irc_privmsg(cmd->parent->bot, channel->name, response);
7179 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7183 static CHANSERV_FUNC(cmd_d)
7185 unsigned long sides, count, modifier, ii, total;
7186 char response[MAXLEN], *sep;
7190 if((count = strtoul(argv[1], &sep, 10)) < 1)
7200 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7201 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7205 else if((sep[0] == '-') && isdigit(sep[1]))
7206 modifier = strtoul(sep, NULL, 10);
7207 else if((sep[0] == '+') && isdigit(sep[1]))
7208 modifier = strtoul(sep+1, NULL, 10);
7215 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7220 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7223 for(total = ii = 0; ii < count; ++ii)
7224 total += (rand() % sides) + 1;
7227 if((count > 1) || modifier)
7229 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7230 sprintf(response, fmt, total, count, sides, modifier);
7234 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7235 sprintf(response, fmt, total, sides);
7238 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7240 send_message_type(4, user, cmd->parent->bot, "%s", response);
7244 static CHANSERV_FUNC(cmd_huggle)
7246 /* CTCP must be via PRIVMSG, never notice */
7248 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7250 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7255 chanserv_adjust_limit(void *data)
7257 struct mod_chanmode change;
7258 struct chanData *cData = data;
7259 struct chanNode *channel = cData->channel;
7262 if(IsSuspended(cData))
7265 cData->limitAdjusted = now;
7266 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7267 if(cData->modes.modes_set & MODE_LIMIT)
7269 if(limit > cData->modes.new_limit)
7270 limit = cData->modes.new_limit;
7271 else if(limit == cData->modes.new_limit)
7275 mod_chanmode_init(&change);
7276 change.modes_set = MODE_LIMIT;
7277 change.new_limit = limit;
7278 mod_chanmode_announce(chanserv, channel, &change);
7282 handle_new_channel(struct chanNode *channel)
7284 struct chanData *cData;
7286 if(!(cData = channel->channel_info))
7289 if(cData->modes.modes_set || cData->modes.modes_clear)
7290 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7292 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7293 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7296 void handle_new_channel_created(char *chan, struct userNode *user) {
7297 if(user->handle_info && chanserv_conf.new_channel_authed) {
7298 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7299 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7300 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7302 if(chanserv_conf.new_channel_msg)
7303 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7306 /* Welcome to my worst nightmare. Warning: Read (or modify)
7307 the code below at your own risk. */
7309 handle_join(struct modeNode *mNode)
7311 struct mod_chanmode change;
7312 struct userNode *user = mNode->user;
7313 struct chanNode *channel = mNode->channel;
7314 struct chanData *cData;
7315 struct userData *uData = NULL;
7316 struct banData *bData;
7317 struct handle_info *handle;
7318 unsigned int modes = 0, info = 0;
7322 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7325 cData = channel->channel_info;
7326 if(channel->members.used > cData->max) {
7327 cData->max = channel->members.used;
7328 cData->max_time = now;
7331 for(i = 0; i < channel->invited.used; i++)
7333 if(channel->invited.list[i] == user) {
7334 userList_remove(&channel->invited, user);
7338 /* Check for bans. If they're joining through a ban, one of two
7340 * 1: Join during a netburst, by riding the break. Kick them
7341 * unless they have ops or voice in the channel.
7342 * 2: They're allowed to join through the ban (an invite in
7343 * ircu2.10, or a +e on Hybrid, or something).
7344 * If they're not joining through a ban, and the banlist is not
7345 * full, see if they're on the banlist for the channel. If so,
7348 if(user->uplink->burst && !mNode->modes)
7351 for(ii = 0; ii < channel->banlist.used; ii++)
7353 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7355 /* Riding a netburst. Naughty. */
7356 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7362 mod_chanmode_init(&change);
7364 if(channel->banlist.used < MAXBANS)
7366 /* Not joining through a ban. */
7367 for(bData = cData->bans;
7368 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7369 bData = bData->next);
7373 char kick_reason[MAXLEN];
7374 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7376 bData->triggered = now;
7377 if(bData != cData->bans)
7379 /* Shuffle the ban to the head of the list. */
7381 bData->next->prev = bData->prev;
7383 bData->prev->next = bData->next;
7386 bData->next = cData->bans;
7389 cData->bans->prev = bData;
7390 cData->bans = bData;
7393 change.args[0].mode = MODE_BAN;
7394 change.args[0].u.hostmask = bData->mask;
7395 mod_chanmode_announce(chanserv, channel, &change);
7396 KickChannelUser(user, channel, chanserv, kick_reason);
7401 /* ChanServ will not modify the limits in join-flooded channels,
7402 or when there are enough slots left below the limit. */
7403 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7404 && !channel->join_flooded
7405 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7407 /* The user count has begun "bumping" into the channel limit,
7408 so set a timer to raise the limit a bit. Any previous
7409 timers are removed so three incoming users within the delay
7410 results in one limit change, not three. */
7412 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7413 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7416 if(channel->join_flooded)
7418 /* don't automatically give ops or voice during a join flood */
7420 else if(cData->lvlOpts[lvlGiveOps] == 0)
7421 modes |= MODE_CHANOP;
7422 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7423 modes |= MODE_VOICE;
7425 greeting = cData->greeting;
7426 if(user->handle_info)
7428 handle = user->handle_info;
7430 if(IsHelper(user) && !IsHelping(user))
7433 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7435 if(channel == chanserv_conf.support_channels.list[ii])
7437 HANDLE_SET_FLAG(user->handle_info, HELPING);
7443 uData = GetTrueChannelAccess(cData, handle);
7444 if(uData && !IsUserSuspended(uData))
7446 /* Ops and above were handled by the above case. */
7447 if(IsUserAutoOp(uData))
7449 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7450 modes |= MODE_CHANOP;
7451 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7452 modes |= MODE_VOICE;
7454 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7455 cData->visited = now;
7456 if(cData->user_greeting)
7457 greeting = cData->user_greeting;
7459 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7460 && ((now - uData->seen) >= chanserv_conf.info_delay)
7468 /* If user joining normally (not during burst), apply op or voice,
7469 * and send greeting/userinfo as appropriate.
7471 if(!user->uplink->burst)
7475 if(modes & MODE_CHANOP)
7476 modes &= ~MODE_VOICE;
7477 change.args[0].mode = modes;
7478 change.args[0].u.member = mNode;
7479 mod_chanmode_announce(chanserv, channel, &change);
7482 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7483 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7484 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7490 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7492 struct mod_chanmode change;
7493 struct userData *channel;
7494 unsigned int ii, jj;
7496 if(!user->handle_info)
7499 mod_chanmode_init(&change);
7501 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7503 struct chanNode *cn;
7504 struct modeNode *mn;
7505 if(IsUserSuspended(channel)
7506 || IsSuspended(channel->channel)
7507 || !(cn = channel->channel->channel))
7510 mn = GetUserMode(cn, user);
7513 if(!IsUserSuspended(channel)
7514 && IsUserAutoInvite(channel)
7515 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7517 && !user->uplink->burst)
7518 irc_invite(chanserv, user, cn);
7522 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7523 channel->channel->visited = now;
7525 if(IsUserAutoOp(channel))
7527 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7528 change.args[0].mode = MODE_CHANOP;
7529 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7530 change.args[0].mode = MODE_VOICE;
7532 change.args[0].mode = 0;
7533 change.args[0].u.member = mn;
7534 if(change.args[0].mode)
7535 mod_chanmode_announce(chanserv, cn, &change);
7538 channel->seen = now;
7539 channel->present = 1;
7542 for(ii = 0; ii < user->channels.used; ++ii)
7544 struct chanNode *chan = user->channels.list[ii]->channel;
7545 struct banData *ban;
7547 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7548 || !chan->channel_info
7549 || IsSuspended(chan->channel_info))
7551 for(jj = 0; jj < chan->banlist.used; ++jj)
7552 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7554 if(jj < chan->banlist.used)
7556 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7558 char kick_reason[MAXLEN];
7559 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7561 change.args[0].mode = MODE_BAN;
7562 change.args[0].u.hostmask = ban->mask;
7563 mod_chanmode_announce(chanserv, chan, &change);
7564 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7565 KickChannelUser(user, chan, chanserv, kick_reason);
7566 ban->triggered = now;
7571 if(IsSupportHelper(user))
7573 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7575 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7577 HANDLE_SET_FLAG(user->handle_info, HELPING);
7585 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7587 struct chanData *cData;
7588 struct userData *uData;
7590 cData = mn->channel->channel_info;
7591 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7594 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7596 /* Allow for a bit of padding so that the limit doesn't
7597 track the user count exactly, which could get annoying. */
7598 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7600 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7601 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7605 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7607 scan_user_presence(uData, mn->user);
7609 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7610 cData->visited = now;
7613 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7616 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7617 struct chanNode *channel;
7618 struct userNode *exclude;
7619 /* When looking at the channel that is being /part'ed, we
7620 * have to skip over the client that is leaving. For
7621 * other channels, we must not do that.
7623 channel = chanserv_conf.support_channels.list[ii];
7624 exclude = (channel == mn->channel) ? mn->user : NULL;
7625 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7628 if(ii == chanserv_conf.support_channels.used)
7629 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7634 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7636 struct userData *uData;
7638 if(!channel->channel_info || !kicker || IsService(kicker)
7639 || (kicker == victim) || IsSuspended(channel->channel_info)
7640 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7643 if(protect_user(victim, kicker, channel->channel_info))
7645 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7646 KickChannelUser(kicker, channel, chanserv, reason);
7649 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7654 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7656 struct chanData *cData;
7658 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7661 cData = channel->channel_info;
7662 if(bad_topic(channel, user, channel->topic))
7664 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7665 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7666 SetChannelTopic(channel, chanserv, old_topic, 1);
7667 else if(cData->topic)
7668 SetChannelTopic(channel, chanserv, cData->topic, 1);
7671 /* With topicsnarf, grab the topic and save it as the default topic. */
7672 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7675 cData->topic = strdup(channel->topic);
7681 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7683 struct mod_chanmode *bounce = NULL;
7684 unsigned int bnc, ii;
7687 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7690 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7691 && mode_lock_violated(&channel->channel_info->modes, change))
7693 char correct[MAXLEN];
7694 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7695 mod_chanmode_format(&channel->channel_info->modes, correct);
7696 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7698 for(ii = bnc = 0; ii < change->argc; ++ii)
7700 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7702 const struct userNode *victim = change->args[ii].u.member->user;
7703 if(!protect_user(victim, user, channel->channel_info))
7706 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7709 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7710 bounce->args[bnc].u.member = GetUserMode(channel, user);
7711 if(bounce->args[bnc].u.member)
7715 bounce->args[bnc].mode = MODE_CHANOP;
7716 bounce->args[bnc].u.member = change->args[ii].u.member;
7718 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7720 else if(change->args[ii].mode & MODE_CHANOP)
7722 const struct userNode *victim = change->args[ii].u.member->user;
7723 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7726 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7727 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7728 bounce->args[bnc].u.member = change->args[ii].u.member;
7731 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7733 const char *ban = change->args[ii].u.hostmask;
7734 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7737 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7738 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7739 bounce->args[bnc].u.hostmask = strdup(ban);
7741 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7746 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7747 mod_chanmode_announce(chanserv, channel, bounce);
7748 for(ii = 0; ii < change->argc; ++ii)
7749 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7750 free((char*)bounce->args[ii].u.hostmask);
7751 mod_chanmode_free(bounce);
7756 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7758 struct chanNode *channel;
7759 struct banData *bData;
7760 struct mod_chanmode change;
7761 unsigned int ii, jj;
7762 char kick_reason[MAXLEN];
7764 mod_chanmode_init(&change);
7766 change.args[0].mode = MODE_BAN;
7767 for(ii = 0; ii < user->channels.used; ++ii)
7769 channel = user->channels.list[ii]->channel;
7770 /* Need not check for bans if they're opped or voiced. */
7771 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7773 /* Need not check for bans unless channel registration is active. */
7774 if(!channel->channel_info || IsSuspended(channel->channel_info))
7776 /* Look for a matching ban already on the channel. */
7777 for(jj = 0; jj < channel->banlist.used; ++jj)
7778 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7780 /* Need not act if we found one. */
7781 if(jj < channel->banlist.used)
7783 /* Look for a matching ban in this channel. */
7784 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7786 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7788 change.args[0].u.hostmask = bData->mask;
7789 mod_chanmode_announce(chanserv, channel, &change);
7790 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7791 KickChannelUser(user, channel, chanserv, kick_reason);
7792 bData->triggered = now;
7793 break; /* we don't need to check any more bans in the channel */
7798 static void handle_rename(struct handle_info *handle, const char *old_handle)
7800 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7804 dict_remove2(handle_dnrs, old_handle, 1);
7805 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7806 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7811 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7813 struct userNode *h_user;
7815 if(handle->channels)
7817 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7818 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7820 while(handle->channels)
7821 del_channel_user(handle->channels, 1);
7826 handle_server_link(UNUSED_ARG(struct server *server))
7828 struct chanData *cData;
7830 for(cData = channelList; cData; cData = cData->next)
7832 if(!IsSuspended(cData))
7833 cData->may_opchan = 1;
7834 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7835 && !cData->channel->join_flooded
7836 && ((cData->channel->limit - cData->channel->members.used)
7837 < chanserv_conf.adjust_threshold))
7839 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7840 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7846 chanserv_conf_read(void)
7850 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7851 struct mod_chanmode *change;
7852 struct string_list *strlist;
7853 struct chanNode *chan;
7856 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7858 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7861 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7862 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7863 chanserv_conf.support_channels.used = 0;
7864 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7866 for(ii = 0; ii < strlist->used; ++ii)
7868 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7871 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7873 channelList_append(&chanserv_conf.support_channels, chan);
7876 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7879 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7882 chan = AddChannel(str, now, str2, NULL);
7884 channelList_append(&chanserv_conf.support_channels, chan);
7886 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7887 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7888 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7889 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7890 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7891 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7892 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7893 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7894 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7895 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7896 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7897 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7898 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7899 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7900 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7901 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7902 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7903 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7904 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7905 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7906 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7907 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7908 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7909 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7910 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7911 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7912 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7913 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7914 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7915 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7916 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7918 NickChange(chanserv, str, 0);
7919 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7920 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7921 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7922 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7923 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7924 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7925 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7926 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7927 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7928 chanserv_conf.max_owned = str ? atoi(str) : 5;
7929 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7930 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7931 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7932 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7933 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7934 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7935 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7936 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7937 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7938 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7939 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7940 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7941 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7944 safestrncpy(mode_line, str, sizeof(mode_line));
7945 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7946 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7947 && (change->argc < 2))
7949 chanserv_conf.default_modes = *change;
7950 mod_chanmode_free(change);
7952 free_string_list(chanserv_conf.set_shows);
7953 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7955 strlist = string_list_copy(strlist);
7958 static const char *list[] = {
7959 /* free form text */
7960 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7961 /* options based on user level */
7962 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7963 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7964 /* multiple choice options */
7965 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7966 /* binary options */
7967 "DynLimit", "NoDelete", "expire", "Vote",
7971 strlist = alloc_string_list(ArrayLength(list)-1);
7972 for(ii=0; list[ii]; ii++)
7973 string_list_append(strlist, strdup(list[ii]));
7975 chanserv_conf.set_shows = strlist;
7976 /* We don't look things up now, in case the list refers to options
7977 * defined by modules initialized after this point. Just mark the
7978 * function list as invalid, so it will be initialized.
7980 set_shows_list.used = 0;
7981 free_string_list(chanserv_conf.eightball);
7982 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7985 strlist = string_list_copy(strlist);
7989 strlist = alloc_string_list(4);
7990 string_list_append(strlist, strdup("Yes."));
7991 string_list_append(strlist, strdup("No."));
7992 string_list_append(strlist, strdup("Maybe so."));
7994 chanserv_conf.eightball = strlist;
7995 free_string_list(chanserv_conf.old_ban_names);
7996 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7998 strlist = string_list_copy(strlist);
8000 strlist = alloc_string_list(2);
8001 chanserv_conf.old_ban_names = strlist;
8002 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
8003 off_channel = str ? atoi(str) : 0;
8005 str = database_get_data(conf_node, "oper_channel", RECDB_QSTRING);
8008 chanserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
8012 chanserv_conf.oper_channel = NULL;
8017 chanserv_note_type_read(const char *key, struct record_data *rd)
8020 struct note_type *ntype;
8023 if(!(obj = GET_RECORD_OBJECT(rd)))
8025 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8028 if(!(ntype = chanserv_create_note_type(key)))
8030 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8034 /* Figure out set access */
8035 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8037 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8038 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8040 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8042 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8043 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8045 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8047 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8051 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8052 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8053 ntype->set_access.min_opserv = 0;
8056 /* Figure out visibility */
8057 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8058 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8059 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8060 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8061 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8062 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8063 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8064 ntype->visible_type = NOTE_VIS_ALL;
8066 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8068 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8069 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8073 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8075 struct vote_option *vOpt;
8078 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8080 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8084 vOpt = calloc(1, sizeof(*vOpt));
8085 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8086 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8087 vOpt->voted = str ? atoi(str) : 0;
8088 vOpt->option_id = str ? atoi(key) : 0;
8089 vOpt->option_str = strdup(key);
8090 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8094 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8096 struct handle_info *handle;
8097 struct userData *uData;
8098 char *seen, *inf, *flags, *voted, *votefor, *expires;
8099 unsigned long last_seen;
8100 unsigned short access_level;
8102 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8104 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8108 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8109 if(access_level > UL_OWNER)
8111 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8115 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8116 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8117 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8118 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8119 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8120 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8121 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8122 handle = get_handle_info(key);
8125 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8129 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8130 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8131 uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8133 if((uData->flags & USER_SUSPENDED) && uData->expires)
8135 if(uData->expires > now)
8136 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8138 uData->flags &= ~USER_SUSPENDED;
8141 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8142 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8150 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8152 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8153 unsigned long set_time, triggered_time, expires_time;
8155 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8157 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8161 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8162 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8163 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8164 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8165 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8166 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8167 if (!reason || !owner)
8170 set_time = set ? strtoul(set, NULL, 0) : now;
8171 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8173 expires_time = strtoul(s_expires, NULL, 0);
8175 expires_time = set_time + atoi(s_duration);
8179 if(!reason || (expires_time && (expires_time < now)))
8182 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8185 static struct suspended *
8186 chanserv_read_suspended(dict_t obj)
8188 struct suspended *suspended = calloc(1, sizeof(*suspended));
8192 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8193 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8194 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8195 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8196 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8197 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8198 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8199 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8200 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8201 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8205 static struct giveownership *
8206 chanserv_read_giveownership(dict_t obj)
8208 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8212 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8213 giveownership->staff_issuer = str ? strdup(str) : NULL;
8215 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8217 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8218 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8220 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8221 giveownership->reason = str ? strdup(str) : NULL;
8222 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8223 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8225 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8226 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8227 return giveownership;
8231 chanserv_channel_read(const char *key, struct record_data *hir)
8233 struct suspended *suspended;
8234 struct giveownership *giveownership;
8235 struct mod_chanmode *modes;
8236 struct chanNode *cNode;
8237 struct chanData *cData;
8238 struct dict *channel, *obj;
8239 char *str, *argv[10];
8243 channel = hir->d.object;
8245 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8248 cNode = AddChannel(key, now, NULL, NULL);
8251 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8254 cData = register_channel(cNode, str);
8257 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8261 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8263 enum levelOption lvlOpt;
8264 enum charOption chOpt;
8266 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8267 cData->flags = atoi(str);
8269 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8271 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8273 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8274 else if(levelOptions[lvlOpt].old_flag)
8276 if(cData->flags & levelOptions[lvlOpt].old_flag)
8277 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8279 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8283 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8285 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8287 cData->chOpts[chOpt] = str[0];
8290 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8292 enum levelOption lvlOpt;
8293 enum charOption chOpt;
8296 cData->flags = base64toint(str, 5);
8297 count = strlen(str += 5);
8298 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8301 if(levelOptions[lvlOpt].old_flag)
8303 if(cData->flags & levelOptions[lvlOpt].old_flag)
8304 lvl = levelOptions[lvlOpt].flag_value;
8306 lvl = levelOptions[lvlOpt].default_value;
8308 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8310 case 'c': lvl = UL_COOWNER; break;
8311 case 'm': lvl = UL_MASTER; break;
8312 case 'n': lvl = UL_OWNER+1; break;
8313 case 'o': lvl = UL_OP; break;
8314 case 'p': lvl = UL_PEON; break;
8315 case 'w': lvl = UL_OWNER; break;
8316 default: lvl = 0; break;
8318 cData->lvlOpts[lvlOpt] = lvl;
8320 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8321 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8324 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8326 cData->expiry = atoi(str);
8327 if(cData->expiry > 0) {
8328 if(cData->expiry > now) {
8329 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8331 timeq_add(1, chanserv_expire_channel, cData);
8338 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8340 suspended = chanserv_read_suspended(obj);
8341 cData->suspended = suspended;
8342 suspended->cData = cData;
8343 /* We could use suspended->expires and suspended->revoked to
8344 * set the CHANNEL_SUSPENDED flag, but we don't. */
8346 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8348 suspended = calloc(1, sizeof(*suspended));
8349 suspended->issued = 0;
8350 suspended->revoked = 0;
8351 suspended->suspender = strdup(str);
8352 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8353 suspended->expires = str ? atoi(str) : 0;
8354 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8355 suspended->reason = strdup(str ? str : "No reason");
8356 suspended->previous = NULL;
8357 cData->suspended = suspended;
8358 suspended->cData = cData;
8362 cData->flags &= ~CHANNEL_SUSPENDED;
8363 suspended = NULL; /* to squelch a warning */
8366 if(IsSuspended(cData)) {
8367 if(suspended->expires > now)
8368 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8369 else if(suspended->expires)
8370 cData->flags &= ~CHANNEL_SUSPENDED;
8373 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8375 giveownership = chanserv_read_giveownership(obj);
8376 cData->giveownership = giveownership;
8379 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8380 struct mod_chanmode change;
8381 mod_chanmode_init(&change);
8383 change.args[0].mode = MODE_CHANOP;
8384 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8385 mod_chanmode_announce(chanserv, cNode, &change);
8388 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8389 cData->registered = str ? strtoul(str, NULL, 0) : now;
8390 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8391 cData->visited = str ? strtoul(str, NULL, 0) : now;
8392 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8393 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8394 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8395 cData->max = str ? atoi(str) : 0;
8396 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8397 cData->max_time = str ? atoi(str) : 0;
8398 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8399 cData->greeting = str ? strdup(str) : NULL;
8400 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8401 cData->user_greeting = str ? strdup(str) : NULL;
8402 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8403 cData->topic_mask = str ? strdup(str) : NULL;
8404 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8405 cData->topic = str ? strdup(str) : NULL;
8407 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8409 cData->vote = str ? strdup(str) : NULL;
8410 dict_delete(cData->vote_options);
8411 cData->vote_options = dict_new();
8412 dict_set_free_data(cData->vote_options, free_vote_options);
8413 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8414 cData->vote_start = str ? atoi(str) : 0;
8415 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8416 for(it = dict_first(obj); it; it = iter_next(it)) {
8417 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8421 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8422 for(it = dict_first(obj); it; it = iter_next(it))
8424 struct record_data *rd = iter_data(it);
8425 if(rd->type != RECDB_QSTRING) continue;
8426 int advtopic_index = atoi(iter_key(it));
8427 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8428 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8431 if(!IsSuspended(cData)
8432 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8433 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8434 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8435 cData->modes = *modes;
8437 cData->modes.modes_set |= MODE_REGISTERED;
8438 if(cData->modes.argc > 1)
8439 cData->modes.argc = 1;
8440 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8441 mod_chanmode_free(modes);
8444 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8445 for(it = dict_first(obj); it; it = iter_next(it))
8446 user_read_helper(iter_key(it), iter_data(it), cData);
8448 if(!cData->users && !IsProtected(cData))
8450 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8451 unregister_channel(cData, "has empty user list.");
8455 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8456 for(it = dict_first(obj); it; it = iter_next(it))
8457 ban_read_helper(iter_key(it), iter_data(it), cData);
8459 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8460 for(it = dict_first(obj); it; it = iter_next(it))
8462 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8463 struct record_data *rd = iter_data(it);
8464 const char *note, *setter;
8466 if(rd->type != RECDB_OBJECT)
8468 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8472 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8474 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8476 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8480 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8481 if(!setter) setter = "<unknown>";
8482 chanserv_add_channel_note(cData, ntype, setter, note);
8490 chanserv_dnr_read(const char *key, struct record_data *hir)
8492 const char *setter, *reason, *str;
8493 struct do_not_register *dnr;
8494 unsigned long expiry;
8496 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8499 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8502 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8505 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8508 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8509 expiry = str ? strtoul(str, NULL, 0) : 0;
8510 if(expiry && expiry <= now)
8512 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8515 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8517 dnr->set = atoi(str);
8523 chanserv_saxdb_read(struct dict *database)
8525 struct dict *section;
8528 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8529 for(it = dict_first(section); it; it = iter_next(it))
8530 chanserv_note_type_read(iter_key(it), iter_data(it));
8532 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8533 for(it = dict_first(section); it; it = iter_next(it))
8534 chanserv_channel_read(iter_key(it), iter_data(it));
8536 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8537 for(it = dict_first(section); it; it = iter_next(it))
8538 chanserv_dnr_read(iter_key(it), iter_data(it));
8544 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8546 int high_present = 0;
8547 saxdb_start_record(ctx, KEY_USERS, 1);
8548 for(; uData; uData = uData->next)
8550 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8552 saxdb_start_record(ctx, uData->handle->handle, 0);
8553 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8554 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8556 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8558 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8559 if(uData->channel->vote && uData->voted)
8560 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8561 if(uData->channel->vote && uData->votefor)
8562 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8564 saxdb_write_string(ctx, KEY_INFO, uData->info);
8565 saxdb_end_record(ctx);
8567 saxdb_end_record(ctx);
8568 return high_present;
8572 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8576 saxdb_start_record(ctx, KEY_BANS, 1);
8577 for(; bData; bData = bData->next)
8579 saxdb_start_record(ctx, bData->mask, 0);
8580 saxdb_write_int(ctx, KEY_SET, bData->set);
8581 if(bData->triggered)
8582 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8584 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8586 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8588 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8589 saxdb_end_record(ctx);
8591 saxdb_end_record(ctx);
8595 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8597 saxdb_start_record(ctx, name, 0);
8598 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8599 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8601 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8603 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8605 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8607 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8608 saxdb_end_record(ctx);
8612 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8614 saxdb_start_record(ctx, name, 0);
8615 if(giveownership->staff_issuer)
8616 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8617 if(giveownership->old_owner)
8618 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8619 if(giveownership->target)
8620 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8621 if(giveownership->target_access)
8622 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8623 if(giveownership->reason)
8624 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8625 if(giveownership->issued)
8626 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8627 if(giveownership->previous)
8628 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8629 saxdb_end_record(ctx);
8633 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8637 enum levelOption lvlOpt;
8638 enum charOption chOpt;
8641 saxdb_start_record(ctx, channel->channel->name, 1);
8643 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8644 saxdb_write_int(ctx, KEY_MAX, channel->max);
8645 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8647 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8648 if(channel->registrar)
8649 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8650 if(channel->greeting)
8651 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8652 if(channel->user_greeting)
8653 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8654 if(channel->topic_mask)
8655 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8656 if(channel->suspended)
8657 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8658 if(channel->giveownership)
8659 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8661 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8664 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8665 if(channel->vote_start)
8666 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8667 if (dict_size(channel->vote_options)) {
8668 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8669 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8670 struct vote_option *vOpt = iter_data(it);
8672 sprintf(str,"%i",vOpt->option_id);
8673 saxdb_start_record(ctx, str, 0);
8675 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8677 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8678 saxdb_end_record(ctx);
8680 saxdb_end_record(ctx);
8684 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8685 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8686 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8687 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8688 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8690 buf[0] = channel->chOpts[chOpt];
8692 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8694 saxdb_end_record(ctx);
8696 if(channel->modes.modes_set || channel->modes.modes_clear)
8698 mod_chanmode_format(&channel->modes, buf);
8699 saxdb_write_string(ctx, KEY_MODES, buf);
8702 high_present = chanserv_write_users(ctx, channel->users);
8703 chanserv_write_bans(ctx, channel->bans);
8705 if(channel->flags & CHANNEL_ADVTOPIC) {
8706 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8708 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8709 if(channel->advtopic[advtopic_index])
8710 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8712 saxdb_end_record(ctx);
8715 if(dict_size(channel->notes))
8719 saxdb_start_record(ctx, KEY_NOTES, 1);
8720 for(it = dict_first(channel->notes); it; it = iter_next(it))
8722 struct note *note = iter_data(it);
8723 saxdb_start_record(ctx, iter_key(it), 0);
8724 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8725 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8726 saxdb_end_record(ctx);
8728 saxdb_end_record(ctx);
8731 if(channel->ownerTransfer)
8732 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8733 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8734 saxdb_end_record(ctx);
8738 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8742 saxdb_start_record(ctx, ntype->name, 0);
8743 switch(ntype->set_access_type)
8745 case NOTE_SET_CHANNEL_ACCESS:
8746 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8748 case NOTE_SET_CHANNEL_SETTER:
8749 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8751 case NOTE_SET_PRIVILEGED: default:
8752 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8755 switch(ntype->visible_type)
8757 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8758 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8759 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8761 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8762 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8763 saxdb_end_record(ctx);
8767 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8769 struct do_not_register *dnr;
8770 dict_iterator_t it, next;
8772 for(it = dict_first(dnrs); it; it = next)
8774 next = iter_next(it);
8775 dnr = iter_data(it);
8776 if(dnr->expires && dnr->expires <= now)
8778 dict_remove(dnrs, iter_key(it));
8781 saxdb_start_record(ctx, dnr->chan_name, 0);
8783 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8785 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8786 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8787 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8788 saxdb_end_record(ctx);
8793 chanserv_saxdb_write(struct saxdb_context *ctx)
8796 struct chanData *channel;
8799 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8800 for(it = dict_first(note_types); it; it = iter_next(it))
8801 chanserv_write_note_type(ctx, iter_data(it));
8802 saxdb_end_record(ctx);
8805 saxdb_start_record(ctx, KEY_DNR, 1);
8806 write_dnrs_helper(ctx, handle_dnrs);
8807 write_dnrs_helper(ctx, plain_dnrs);
8808 write_dnrs_helper(ctx, mask_dnrs);
8809 saxdb_end_record(ctx);
8812 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8813 for(channel = channelList; channel; channel = channel->next)
8814 chanserv_write_channel(ctx, channel);
8815 saxdb_end_record(ctx);
8821 chanserv_db_cleanup(void) {
8823 unreg_part_func(handle_part);
8825 unregister_channel(channelList, "terminating.");
8826 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8827 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8828 free(chanserv_conf.support_channels.list);
8829 dict_delete(handle_dnrs);
8830 dict_delete(plain_dnrs);
8831 dict_delete(mask_dnrs);
8832 dict_delete(note_types);
8833 free_string_list(chanserv_conf.eightball);
8834 free_string_list(chanserv_conf.old_ban_names);
8835 free_string_list(chanserv_conf.set_shows);
8836 free(set_shows_list.list);
8837 free(uset_shows_list.list);
8840 struct userData *helper = helperList;
8841 helperList = helperList->next;
8846 #if defined(GCC_VARMACROS)
8847 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8848 #elif defined(C99_VARMACROS)
8849 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8851 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8852 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8855 init_chanserv(const char *nick)
8857 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8858 conf_register_reload(chanserv_conf_read);
8862 reg_server_link_func(handle_server_link);
8863 reg_new_channel_func(handle_new_channel);
8864 reg_join_func(handle_join);
8865 reg_part_func(handle_part);
8866 reg_kick_func(handle_kick);
8867 reg_topic_func(handle_topic);
8868 reg_mode_change_func(handle_mode);
8869 reg_nick_change_func(handle_nick_change);
8870 reg_auth_func(handle_auth);
8873 reg_handle_rename_func(handle_rename);
8874 reg_unreg_func(handle_unreg);
8876 handle_dnrs = dict_new();
8877 dict_set_free_data(handle_dnrs, free);
8878 plain_dnrs = dict_new();
8879 dict_set_free_data(plain_dnrs, free);
8880 mask_dnrs = dict_new();
8881 dict_set_free_data(mask_dnrs, free);
8883 reg_svccmd_unbind_func(handle_svccmd_unbind);
8884 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8885 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8886 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8887 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8888 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8889 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8890 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8891 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8892 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8893 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8894 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8895 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8896 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8898 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8899 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8901 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8902 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8903 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8904 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8905 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8907 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8908 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8909 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8910 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8911 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8913 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8914 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8915 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8916 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8918 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8919 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8920 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8921 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8922 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8923 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8924 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8925 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8927 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8928 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8929 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8930 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8931 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8932 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8933 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8934 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8935 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8936 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8937 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8938 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8939 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8940 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8941 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8943 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8944 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8945 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8946 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8947 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8949 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8950 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8952 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8953 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8954 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8955 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8956 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8957 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8958 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8959 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8960 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8961 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8962 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8964 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8965 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8967 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8968 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8969 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8970 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8972 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8973 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8974 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8975 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8976 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8978 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8979 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8980 DEFINE_COMMAND(pong, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8981 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8982 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8983 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8984 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8986 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8987 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8988 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8989 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8990 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8991 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8992 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8993 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8995 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8997 /* Channel options */
8998 DEFINE_CHANNEL_OPTION(defaulttopic);
8999 DEFINE_CHANNEL_OPTION(topicmask);
9000 DEFINE_CHANNEL_OPTION(greeting);
9001 DEFINE_CHANNEL_OPTION(usergreeting);
9002 DEFINE_CHANNEL_OPTION(modes);
9003 DEFINE_CHANNEL_OPTION(enfops);
9004 DEFINE_CHANNEL_OPTION(giveops);
9005 DEFINE_CHANNEL_OPTION(protect);
9006 DEFINE_CHANNEL_OPTION(enfmodes);
9007 DEFINE_CHANNEL_OPTION(enftopic);
9008 DEFINE_CHANNEL_OPTION(pubcmd);
9009 DEFINE_CHANNEL_OPTION(givevoice);
9010 DEFINE_CHANNEL_OPTION(userinfo);
9011 DEFINE_CHANNEL_OPTION(dynlimit);
9012 DEFINE_CHANNEL_OPTION(topicsnarf);
9013 DEFINE_CHANNEL_OPTION(vote);
9014 DEFINE_CHANNEL_OPTION(nodelete);
9015 DEFINE_CHANNEL_OPTION(toys);
9016 DEFINE_CHANNEL_OPTION(setters);
9017 DEFINE_CHANNEL_OPTION(topicrefresh);
9018 DEFINE_CHANNEL_OPTION(ctcpusers);
9019 DEFINE_CHANNEL_OPTION(ctcpreaction);
9020 DEFINE_CHANNEL_OPTION(inviteme);
9021 DEFINE_CHANNEL_OPTION(advtopic);
9022 DEFINE_CHANNEL_OPTION(unreviewed);
9023 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
9024 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9025 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9027 DEFINE_CHANNEL_OPTION(offchannel);
9028 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9030 /* Alias set topic to set defaulttopic for compatibility. */
9031 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9034 DEFINE_USER_OPTION(noautoop);
9035 DEFINE_USER_OPTION(autoinvite);
9036 DEFINE_USER_OPTION(info);
9038 /* Alias uset autovoice to uset autoop. */
9039 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9041 note_types = dict_new();
9042 dict_set_free_data(note_types, chanserv_deref_note_type);
9045 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9046 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9047 service_register(chanserv)->trigger = '!';
9048 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9050 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9052 if(chanserv_conf.channel_expire_frequency)
9053 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9055 if(chanserv_conf.dnr_expire_frequency)
9056 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9058 if(chanserv_conf.refresh_period)
9060 unsigned long next_refresh;
9061 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9062 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9065 reg_exit_func(chanserv_db_cleanup);
9066 message_register_table(msgtab);