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 if(devnull_user_has_priv(user->handle_info, DEVNULL_PRIV_OPME))
3292 change.args[0].mode = MODE_CHANOP;
3293 errmsg = "CSMSG_ALREADY_OPPED";
3298 reply("CSMSG_NO_ACCESS");
3301 change.args[0].mode &= ~change.args[0].u.member->modes;
3302 if(!change.args[0].mode)
3305 reply(errmsg, channel->name);
3308 modcmd_chanmode_announce(&change);
3313 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3319 for(ii=0; ii<channel->members.used; ii++)
3321 struct modeNode *mn = channel->members.list[ii];
3323 if(IsService(mn->user))
3326 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3329 if(protect_user(mn->user, user, channel->channel_info))
3333 victims[(*victimCount)++] = mn;
3339 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3341 struct userNode *victim;
3342 struct modeNode **victims;
3343 unsigned int offset, n, victimCount, duration = 0;
3344 char *reason = "Bye.", *ban, *name;
3345 char interval[INTERVALLEN];
3347 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3348 REQUIRE_PARAMS(offset);
3349 if(argc > offset && IsNetServ(user))
3351 if(*argv[offset] == '$') {
3352 struct userNode *hib;
3353 const char *accountnameb = argv[offset] + 1;
3354 if(!(hib = GetUserH(accountnameb)))
3356 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3365 reason = unsplit_string(argv + offset, argc - offset, NULL);
3366 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3368 /* Truncate the reason to a length of TOPICLEN, as
3369 the ircd does; however, leave room for an ellipsis
3370 and the kicker's nick. */
3371 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3375 if((victim = GetUserH(argv[1])))
3377 victims = alloca(sizeof(victims[0]));
3378 victims[0] = GetUserMode(channel, victim);
3379 /* XXX: The comparison with ACTION_KICK is just because all
3380 * other actions can work on users outside the channel, and we
3381 * want to allow those (e.g. unbans) in that case. If we add
3382 * some other ejection action for in-channel users, change
3384 victimCount = victims[0] ? 1 : 0;
3386 if(IsService(victim))
3388 reply("MSG_SERVICE_IMMUNE", victim->nick);
3392 if((action == ACTION_KICK) && !victimCount)
3394 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3398 if(protect_user(victim, user, channel->channel_info))
3400 reply("CSMSG_USER_PROTECTED", victim->nick);
3404 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3405 name = victim->nick;
3407 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3409 struct handle_info *hi;
3410 extern const char *titlehost_suffix;
3411 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3412 const char *accountname = argv[1] + 1;
3414 if(!(hi = get_handle_info(accountname)))
3416 reply("MSG_HANDLE_UNKNOWN", accountname);
3420 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3421 victims = alloca(sizeof(victims[0]) * channel->members.used);
3423 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3425 reply("CSMSG_MASK_PROTECTED", banmask);
3429 if((action == ACTION_KICK) && (victimCount == 0))
3431 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3435 name = ban = strdup(banmask);
3439 if(!is_ircmask(argv[1]))
3441 reply("MSG_NICK_UNKNOWN", argv[1]);
3445 victims = alloca(sizeof(victims[0]) * channel->members.used);
3447 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3449 reply("CSMSG_MASK_PROTECTED", argv[1]);
3453 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3455 reply("CSMSG_LAME_MASK", argv[1]);
3459 if((action == ACTION_KICK) && (victimCount == 0))
3461 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3465 name = ban = strdup(argv[1]);
3468 /* Truncate the ban in place if necessary; we must ensure
3469 that 'ban' is a valid ban mask before sanitizing it. */
3470 sanitize_ircmask(ban);
3472 if(action & ACTION_ADD_BAN)
3474 struct banData *bData, *next;
3476 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3478 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3483 if(action & ACTION_ADD_TIMED_BAN)
3485 duration = ParseInterval(argv[2]);
3487 if(duration < chanserv_conf.min_time_bans)
3489 reply("CSMSG_DURATION_TOO_LOW");
3493 else if(duration > (86400 * 365 * 2))
3495 reply("CSMSG_DURATION_TOO_HIGH");
3501 for(bData = channel->channel_info->bans; bData; bData = next)
3503 if(match_ircglobs(bData->mask, ban))
3505 int exact = !irccasecmp(bData->mask, ban);
3507 /* The ban is redundant; there is already a ban
3508 with the same effect in place. */
3512 free(bData->reason);
3513 bData->reason = strdup(reason);
3514 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3516 reply("CSMSG_REASON_CHANGE", ban);
3520 if(exact && bData->expires)
3524 /* If the ban matches an existing one exactly,
3525 extend the expiration time if the provided
3526 duration is longer. */
3527 if(duration && (now + duration > bData->expires))
3529 bData->expires = now + duration;
3540 /* Delete the expiration timeq entry and
3541 requeue if necessary. */
3542 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3545 timeq_add(bData->expires, expire_ban, bData);
3549 /* automated kickban */
3552 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3554 reply("CSMSG_BAN_ADDED", name, channel->name);
3560 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3567 if(match_ircglobs(ban, bData->mask))
3569 /* The ban we are adding makes previously existing
3570 bans redundant; silently remove them. */
3571 del_channel_ban(bData);
3575 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);
3577 name = ban = strdup(bData->mask);
3581 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3583 extern const char *hidden_host_suffix;
3584 const char *old_name = chanserv_conf.old_ban_names->list[n];
3586 unsigned int l1, l2;
3589 l2 = strlen(old_name);
3592 if(irccasecmp(ban + l1 - l2, old_name))
3594 new_mask = malloc(MAXLEN);
3595 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3597 name = ban = new_mask;
3602 if(action & ACTION_BAN)
3604 unsigned int exists;
3605 struct mod_chanmode *change;
3607 if(channel->banlist.used >= MAXBANS)
3610 reply("CSMSG_BANLIST_FULL", channel->name);
3615 exists = ChannelBanExists(channel, ban);
3616 change = mod_chanmode_alloc(victimCount + 1);
3617 for(n = 0; n < victimCount; ++n)
3619 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3620 change->args[n].u.member = victims[n];
3624 change->args[n].mode = MODE_BAN;
3625 change->args[n++].u.hostmask = ban;
3629 modcmd_chanmode_announce(change);
3631 mod_chanmode_announce(chanserv, channel, change);
3632 mod_chanmode_free(change);
3634 if(exists && (action == ACTION_BAN))
3637 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3643 if(action & ACTION_KICK)
3645 char kick_reason[MAXLEN];
3646 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3648 for(n = 0; n < victimCount; n++)
3649 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3654 /* No response, since it was automated. */
3656 else if(action & ACTION_ADD_BAN)
3659 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3661 reply("CSMSG_BAN_ADDED", name, channel->name);
3663 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3664 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3665 else if(action & ACTION_BAN)
3666 reply("CSMSG_BAN_DONE", name, channel->name);
3667 else if(action & ACTION_KICK && victimCount)
3668 reply("CSMSG_KICK_DONE", name, channel->name);
3674 static CHANSERV_FUNC(cmd_kickban)
3676 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3679 static CHANSERV_FUNC(cmd_kick)
3681 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3684 static CHANSERV_FUNC(cmd_ban)
3686 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3689 static CHANSERV_FUNC(cmd_addban)
3691 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3694 static CHANSERV_FUNC(cmd_addtimedban)
3696 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3699 struct mod_chanmode *
3700 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3702 struct mod_chanmode *change;
3703 unsigned char *match;
3704 unsigned int ii, count;
3706 match = alloca(bans->used);
3709 for(ii = count = 0; ii < bans->used; ++ii)
3711 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3712 MATCH_USENICK | MATCH_VISIBLE);
3719 for(ii = count = 0; ii < bans->used; ++ii)
3721 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3728 change = mod_chanmode_alloc(count);
3729 for(ii = count = 0; ii < bans->used; ++ii)
3733 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3734 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3736 assert(count == change->argc);
3741 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3743 struct userNode *actee;
3749 /* may want to allow a comma delimited list of users... */
3750 if(!(actee = GetUserH(argv[1])))
3752 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3754 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3755 const char *accountname = argv[1] + 1;
3757 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3758 mask = strdup(banmask);
3760 else if(!is_ircmask(argv[1]))
3762 reply("MSG_NICK_UNKNOWN", argv[1]);
3767 mask = strdup(argv[1]);
3771 /* We don't sanitize the mask here because ircu
3773 if(action & ACTION_UNBAN)
3775 struct mod_chanmode *change;
3776 change = find_matching_bans(&channel->banlist, actee, mask);
3781 modcmd_chanmode_announce(change);
3782 for(ii = 0; ii < change->argc; ++ii)
3783 free((char*)change->args[ii].u.hostmask);
3784 mod_chanmode_free(change);
3789 if(action & ACTION_DEL_BAN)
3791 struct banData *ban, *next;
3793 ban = channel->channel_info->bans;
3797 for( ; ban && !user_matches_glob(actee, ban->mask,
3798 MATCH_USENICK | MATCH_VISIBLE);
3801 for( ; ban && !match_ircglobs(mask, ban->mask);
3806 del_channel_ban(ban);
3813 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3815 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3821 static CHANSERV_FUNC(cmd_unban)
3823 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3826 static CHANSERV_FUNC(cmd_delban)
3828 /* it doesn't necessarily have to remove the channel ban - may want
3829 to make that an option. */
3830 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3833 static CHANSERV_FUNC(cmd_unbanme)
3835 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3836 long flags = ACTION_UNBAN;
3838 /* remove permanent bans if the user has the proper access. */
3839 if(uData->access >= UL_MASTER)
3840 flags |= ACTION_DEL_BAN;
3842 argv[1] = user->nick;
3843 return unban_user(user, channel, 2, argv, cmd, flags);
3846 static CHANSERV_FUNC(cmd_unbanall)
3848 struct mod_chanmode *change;
3851 if(!channel->banlist.used)
3853 reply("CSMSG_NO_BANS", channel->name);
3857 change = mod_chanmode_alloc(channel->banlist.used);
3858 for(ii=0; ii<channel->banlist.used; ii++)
3860 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3861 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3863 modcmd_chanmode_announce(change);
3864 for(ii = 0; ii < change->argc; ++ii)
3865 free((char*)change->args[ii].u.hostmask);
3866 mod_chanmode_free(change);
3867 reply("CSMSG_BANS_REMOVED", channel->name);
3871 static CHANSERV_FUNC(cmd_open)
3873 struct mod_chanmode *change;
3876 change = find_matching_bans(&channel->banlist, user, NULL);
3878 change = mod_chanmode_alloc(0);
3879 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3880 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3881 && channel->channel_info->modes.modes_set)
3882 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3883 modcmd_chanmode_announce(change);
3884 reply("CSMSG_CHANNEL_OPENED", channel->name);
3885 for(ii = 0; ii < change->argc; ++ii)
3886 free((char*)change->args[ii].u.hostmask);
3887 mod_chanmode_free(change);
3891 static CHANSERV_FUNC(cmd_myaccess)
3893 static struct string_buffer sbuf;
3894 struct handle_info *target_handle;
3895 struct userData *uData;
3900 target_handle = user->handle_info;
3901 else if(!IsStaff(user))
3903 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3906 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3909 if(!oper_outranks(user, target_handle))
3912 if(!target_handle->channels)
3914 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3918 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3919 for(uData = target_handle->channels; uData; uData = uData->u_next)
3921 struct chanData *cData = uData->channel;
3923 unsigned int base_len;
3925 if(uData->access > UL_OWNER)
3927 if(uData->access == UL_OWNER)
3930 if(IsProtected(cData)
3931 && (target_handle != user->handle_info)
3932 && !GetTrueChannelAccess(cData, user->handle_info)
3933 && !IsNetworkHelper(user))
3936 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3937 base_len = sbuf.used;
3938 if(IsUserSuspended(uData))
3939 string_buffer_append(&sbuf, 's');
3940 if(IsUserAutoOp(uData))
3942 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3943 string_buffer_append(&sbuf, 'o');
3944 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3945 string_buffer_append(&sbuf, 'v');
3947 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3948 string_buffer_append(&sbuf, 'i');
3949 if(sbuf.used==base_len)
3952 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3954 string_buffer_append_string(&sbuf, ")]");
3955 string_buffer_append(&sbuf, '\0');
3956 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3960 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3962 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3968 static CHANSERV_FUNC(cmd_access)
3970 struct userNode *target;
3971 struct handle_info *target_handle;
3972 struct userData *uData;
3974 char prefix[MAXLEN];
3979 target_handle = target->handle_info;
3981 else if((target = GetUserH(argv[1])))
3983 target_handle = target->handle_info;
3985 else if(argv[1][0] == '*')
3987 if(!(target_handle = get_handle_info(argv[1]+1)))
3989 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3995 reply("MSG_NICK_UNKNOWN", argv[1]);
3999 assert(target || target_handle);
4001 if(target == chanserv)
4003 reply("CSMSG_IS_CHANSERV");
4011 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4016 reply("MSG_USER_AUTHENTICATE", target->nick);
4019 reply("MSG_AUTHENTICATE");
4025 const char *epithet = NULL, *type = NULL;
4028 epithet = chanserv_conf.irc_operator_epithet;
4029 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4031 else if(IsNetworkHelper(target))
4033 epithet = chanserv_conf.network_helper_epithet;
4034 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4036 else if(IsSupportHelper(target))
4038 epithet = chanserv_conf.support_helper_epithet;
4039 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4043 if(target_handle->epithet)
4044 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4046 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4048 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4052 sprintf(prefix, "%s", target_handle->handle);
4055 if(!channel->channel_info)
4057 reply("CSMSG_NOT_REGISTERED", channel->name);
4061 helping = HANDLE_FLAGGED(target_handle, HELPING)
4062 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4063 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4065 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4066 /* To prevent possible information leaks, only show infolines
4067 * if the requestor is in the channel or it's their own
4069 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4071 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4073 /* Likewise, only say it's suspended if the user has active
4074 * access in that channel or it's their own entry. */
4075 if(IsUserSuspended(uData)
4076 && (GetChannelUser(channel->channel_info, user->handle_info)
4077 || (user->handle_info == uData->handle)))
4079 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4084 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4091 def_list(struct listData *list)
4095 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4097 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4098 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4099 if(list->table.length == 1)
4101 msg = user_find_message(list->user, "MSG_NONE");
4102 send_message_type(4, list->user, list->bot, " %s", msg);
4107 userData_access_comp(const void *arg_a, const void *arg_b)
4109 const struct userData *a = *(struct userData**)arg_a;
4110 const struct userData *b = *(struct userData**)arg_b;
4112 if(a->access != b->access)
4113 res = b->access - a->access;
4115 res = irccasecmp(a->handle->handle, b->handle->handle);
4120 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4122 void (*send_list)(struct listData *);
4123 struct userData *uData;
4124 struct listData lData;
4125 unsigned int matches;
4129 lData.bot = cmd->parent->bot;
4130 lData.channel = channel;
4131 lData.lowest = lowest;
4132 lData.highest = highest;
4133 lData.search = (argc > 1) ? argv[1] : NULL;
4134 send_list = def_list;
4136 if(user->handle_info)
4138 switch(user->handle_info->userlist_style)
4140 case HI_STYLE_DEF: send_list = def_list; break;
4141 case HI_STYLE_ZOOT: send_list = def_list; break;
4145 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4147 for(uData = channel->channel_info->users; uData; uData = uData->next)
4149 if((uData->access < lowest)
4150 || (uData->access > highest)
4151 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4153 lData.users[matches++] = uData;
4155 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4157 lData.table.length = matches+1;
4158 lData.table.width = 4;
4159 lData.table.flags = TABLE_NO_FREE;
4160 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4161 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4162 lData.table.contents[0] = ary;
4165 ary[2] = "Last Seen";
4167 for(matches = 1; matches < lData.table.length; ++matches)
4169 char seen[INTERVALLEN];
4171 uData = lData.users[matches-1];
4172 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4173 lData.table.contents[matches] = ary;
4174 ary[0] = strtab(uData->access);
4175 ary[1] = uData->handle->handle;
4178 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4180 else if(!uData->seen)
4183 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4184 ary[2] = strdup(ary[2]);
4185 if(IsUserSuspended(uData))
4186 ary[3] = "Suspended";
4187 else if(HANDLE_FLAGGED(uData->handle, OPER))
4188 ary[3] = "Operator";
4189 else if(HANDLE_FLAGGED(uData->handle, HELPING))
4191 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4193 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4194 ary[3] = "Vacation";
4195 else if(HANDLE_FLAGGED(uData->handle, BOT))
4201 for(matches = 1; matches < lData.table.length; ++matches)
4203 free((char*)lData.table.contents[matches][2]);
4204 free(lData.table.contents[matches]);
4206 free(lData.table.contents[0]);
4207 free(lData.table.contents);
4208 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4212 static CHANSERV_FUNC(cmd_users)
4214 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4217 static CHANSERV_FUNC(cmd_wlist)
4219 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4222 static CHANSERV_FUNC(cmd_clist)
4224 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4227 static CHANSERV_FUNC(cmd_mlist)
4229 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4232 static CHANSERV_FUNC(cmd_olist)
4234 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4237 static CHANSERV_FUNC(cmd_plist)
4239 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4242 static CHANSERV_FUNC(cmd_bans)
4244 struct userNode *search_u = NULL;
4245 struct helpfile_table tbl;
4246 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4247 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4248 const char *msg_never, *triggered, *expires;
4249 struct banData *ban, **bans;
4253 else if(strchr(search = argv[1], '!'))
4256 search_wilds = search[strcspn(search, "?*")];
4258 else if(!(search_u = GetUserH(search)))
4259 reply("MSG_NICK_UNKNOWN", search);
4261 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4263 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4267 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4272 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4275 bans[matches++] = ban;
4280 tbl.length = matches + 1;
4281 tbl.width = 4 + timed;
4283 tbl.flags = TABLE_NO_FREE;
4284 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4285 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4286 tbl.contents[0][0] = "Mask";
4287 tbl.contents[0][1] = "Set By";
4288 tbl.contents[0][2] = "Triggered";
4291 tbl.contents[0][3] = "Expires";
4292 tbl.contents[0][4] = "Reason";
4295 tbl.contents[0][3] = "Reason";
4298 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4300 free(tbl.contents[0]);
4305 msg_never = user_find_message(user, "MSG_NEVER");
4306 for(ii = 0; ii < matches; )
4312 else if(ban->expires)
4313 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4315 expires = msg_never;
4318 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4320 triggered = msg_never;
4322 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4323 tbl.contents[ii][0] = ban->mask;
4324 tbl.contents[ii][1] = ban->owner;
4325 tbl.contents[ii][2] = strdup(triggered);
4328 tbl.contents[ii][3] = strdup(expires);
4329 tbl.contents[ii][4] = ban->reason;
4332 tbl.contents[ii][3] = ban->reason;
4334 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4335 reply("MSG_MATCH_COUNT", matches);
4336 for(ii = 1; ii < tbl.length; ++ii)
4338 free((char*)tbl.contents[ii][2]);
4340 free((char*)tbl.contents[ii][3]);
4341 free(tbl.contents[ii]);
4343 free(tbl.contents[0]);
4349 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4351 struct chanData *cData = channel->channel_info;
4352 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4354 if(cData->topic_mask)
4356 if(cData->flags & CHANNEL_ADVTOPIC)
4358 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4359 char topicmask[TOPICLEN];
4360 int skipnum, topicpos = 0;
4361 char *ptr = cData->topic_mask;
4362 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4365 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4367 topicmask[topicpos++] = '*';
4369 topicmask[topicpos++] = *ptr;
4372 topicmask[topicpos++] = *ptr;
4376 topicmask[topicpos] = 0;
4377 return !match_ircglob(new_topic, topicmask);
4380 return !match_ircglob(new_topic, cData->topic_mask);
4382 else if(cData->topic)
4383 return irccasecmp(new_topic, cData->topic);
4388 static CHANSERV_FUNC(cmd_topic)
4390 struct chanData *cData;
4393 cData = channel->channel_info;
4398 SetChannelTopic(channel, chanserv, cData->topic, 1);
4399 reply("CSMSG_TOPIC_SET", cData->topic);
4403 reply("CSMSG_NO_TOPIC", channel->name);
4407 topic = unsplit_string(argv + 1, argc - 1, NULL);
4408 /* If they say "!topic *", use an empty topic. */
4409 if((topic[0] == '*') && (topic[1] == 0))
4411 if(bad_topic(channel, user, topic))
4413 char *topic_mask = cData->topic_mask;
4416 char new_topic[TOPICLEN+1], tchar;
4417 int pos=0, starpos=-1, dpos=0, len;
4419 if(cData->flags & CHANNEL_ADVTOPIC)
4421 //first check if there is a leading 'modifier id'
4422 int advtopic_index = 0;
4425 for(; topic[pos]; pos++)
4427 if(topic[pos] == ' ')
4429 //leading number found, cut off and store value in advtopic_index
4431 advtopic_index = atoi(topic) - 1; //no zerobase
4432 topic = &topic[pos+1];
4433 /* If they say "!topic 2 *", unset advtopic id 2. */
4434 if((topic[0] == '*') && (topic[1] == 0))
4437 if(!isdigit(topic[pos]))
4440 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4443 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4446 if(cData->advtopic[advtopic_index])
4447 free(cData->advtopic[advtopic_index]);
4448 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4449 char *ptr = topic_mask;
4450 while(*ptr && (dpos <= TOPICLEN))
4456 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4457 numbuf[numpos++] = *ptr;
4460 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4462 new_topic[dpos++] = *ptr; //is % again
4465 advtopic_index--; //no zero base
4466 if(!cData->advtopic[advtopic_index])
4467 break; //just leave it empty
4468 len = strlen(cData->advtopic[advtopic_index]);
4469 if((dpos + len) > TOPICLEN)
4470 len = TOPICLEN + 1 - dpos;
4471 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4475 ptr++; /* and fall through */
4478 new_topic[dpos++] = *ptr;
4484 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4491 len = strlen(topic);
4492 if((dpos + len) > TOPICLEN)
4493 len = TOPICLEN + 1 - dpos;
4494 memcpy(new_topic+dpos, topic, len);
4498 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4499 default: new_topic[dpos++] = tchar; break;
4502 if((dpos > TOPICLEN) || tchar)
4505 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4506 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4510 new_topic[dpos] = 0;
4511 SetChannelTopic(channel, chanserv, new_topic, 1);
4513 reply("CSMSG_TOPIC_LOCKED", channel->name);
4518 SetChannelTopic(channel, chanserv, topic, 1);
4520 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4522 /* Grab the topic and save it as the default topic. */
4524 cData->topic = strdup(channel->topic);
4530 static CHANSERV_FUNC(cmd_mode)
4532 struct userData *uData;
4533 struct mod_chanmode *change;
4539 change = &channel->channel_info->modes;
4540 if(change->modes_set || change->modes_clear) {
4541 modcmd_chanmode_announce(change);
4542 reply("CSMSG_DEFAULTED_MODES", channel->name);
4544 reply("CSMSG_NO_MODES", channel->name);
4548 uData = GetChannelUser(channel->channel_info, user->handle_info);
4550 base_oplevel = MAXOPLEVEL;
4551 else if (uData->access >= UL_OWNER)
4554 base_oplevel = 1 + UL_OWNER - uData->access;
4555 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4558 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4562 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4563 && mode_lock_violated(&channel->channel_info->modes, change))
4566 mod_chanmode_format(&channel->channel_info->modes, modes);
4567 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4571 modcmd_chanmode_announce(change);
4572 mod_chanmode_format(change, fmt);
4573 mod_chanmode_free(change);
4574 reply("CSMSG_MODES_SET", fmt);
4579 chanserv_del_invite_mark(void *data)
4581 struct ChanUser *chanuser = data;
4582 struct chanNode *channel = chanuser->chan;
4584 if(!channel) return;
4585 for(i = 0; i < channel->invited.used; i++)
4587 if(channel->invited.list[i] == chanuser->user) {
4588 userList_remove(&channel->invited, chanuser->user);
4594 static CHANSERV_FUNC(cmd_invite)
4596 struct userNode *invite;
4597 struct ChanUser *chanuser;
4602 if(!(invite = GetUserH(argv[1])))
4604 reply("MSG_NICK_UNKNOWN", argv[1]);
4611 if(GetUserMode(channel, invite))
4613 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4617 for(i = 0; i < channel->invited.used; i++)
4619 if(channel->invited.list[i] == invite) {
4620 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4629 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4630 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4633 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4635 irc_invite(chanserv, invite, channel);
4637 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4639 userList_append(&channel->invited, invite);
4640 chanuser = calloc(1, sizeof(*chanuser));
4641 chanuser->user=invite;
4642 chanuser->chan=channel;
4643 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4648 static CHANSERV_FUNC(cmd_inviteme)
4650 if(GetUserMode(channel, user))
4652 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4655 if(channel->channel_info
4656 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4658 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4661 irc_invite(cmd->parent->bot, user, channel);
4665 static CHANSERV_FUNC(cmd_invitemeall)
4667 struct handle_info *target = user->handle_info;
4668 struct userData *uData;
4670 if(!target->channels)
4672 reply("CSMSG_SQUAT_ACCESS", target->handle);
4676 for(uData = target->channels; uData; uData = uData->u_next)
4678 struct chanData *cData = uData->channel;
4679 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4681 irc_invite(cmd->parent->bot, user, cData->channel);
4688 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4691 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4693 /* We display things based on two dimensions:
4694 * - Issue time: present or absent
4695 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4696 * (in order of precedence, so something both expired and revoked
4697 * only counts as revoked)
4699 combo = (suspended->issued ? 4 : 0)
4700 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4702 case 0: /* no issue time, indefinite expiration */
4703 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4705 case 1: /* no issue time, expires in future */
4706 intervalString(buf1, suspended->expires-now, user->handle_info);
4707 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4709 case 2: /* no issue time, expired */
4710 intervalString(buf1, now-suspended->expires, user->handle_info);
4711 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4713 case 3: /* no issue time, revoked */
4714 intervalString(buf1, now-suspended->revoked, user->handle_info);
4715 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4717 case 4: /* issue time set, indefinite expiration */
4718 intervalString(buf1, now-suspended->issued, user->handle_info);
4719 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4721 case 5: /* issue time set, expires in future */
4722 intervalString(buf1, now-suspended->issued, user->handle_info);
4723 intervalString(buf2, suspended->expires-now, user->handle_info);
4724 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4726 case 6: /* issue time set, expired */
4727 intervalString(buf1, now-suspended->issued, user->handle_info);
4728 intervalString(buf2, now-suspended->expires, user->handle_info);
4729 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4731 case 7: /* issue time set, revoked */
4732 intervalString(buf1, now-suspended->issued, user->handle_info);
4733 intervalString(buf2, now-suspended->revoked, user->handle_info);
4734 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4737 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4743 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4746 const char *fmt = "%a %b %d %H:%M %Y";
4747 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4749 if(giveownership->staff_issuer)
4751 if(giveownership->reason)
4752 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4753 giveownership->target, giveownership->target_access,
4754 giveownership->staff_issuer, buf, giveownership->reason);
4756 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4757 giveownership->target, giveownership->target_access,
4758 giveownership->staff_issuer, buf);
4762 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4766 static CHANSERV_FUNC(cmd_info)
4768 char modes[MAXLEN], buffer[INTERVALLEN];
4769 struct userData *uData, *owner;
4770 struct chanData *cData;
4771 struct do_not_register *dnr;
4776 cData = channel->channel_info;
4777 reply("CSMSG_CHANNEL_INFO", channel->name);
4779 uData = GetChannelUser(cData, user->handle_info);
4780 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4782 mod_chanmode_format(&cData->modes, modes);
4783 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4784 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4787 for(it = dict_first(cData->notes); it; it = iter_next(it))
4791 note = iter_data(it);
4792 if(!note_type_visible_to_user(cData, note->type, user))
4795 padding = PADLEN - 1 - strlen(iter_key(it));
4796 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4799 if(cData->max_time) {
4800 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4802 reply("CSMSG_CHANNEL_MAX", cData->max);
4804 for(owner = cData->users; owner; owner = owner->next)
4805 if(owner->access == UL_OWNER)
4806 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4807 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4808 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4809 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4811 privileged = IsStaff(user);
4813 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4814 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4815 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4817 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4818 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4820 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4822 struct suspended *suspended;
4823 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4824 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4825 show_suspension_info(cmd, user, suspended);
4827 else if(IsSuspended(cData))
4829 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4830 show_suspension_info(cmd, user, cData->suspended);
4833 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4835 struct giveownership *giveownership;
4836 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4837 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4838 show_giveownership_info(cmd, user, giveownership);
4843 static CHANSERV_FUNC(cmd_netinfo)
4845 extern unsigned long boot_time;
4846 extern unsigned long burst_length;
4847 char interval[INTERVALLEN];
4849 reply("CSMSG_NETWORK_INFO");
4850 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4851 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4852 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4853 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4854 reply("CSMSG_NETWORK_BANS", banCount);
4855 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4856 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4857 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4862 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4864 struct helpfile_table table;
4866 struct userNode *user;
4871 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4872 table.contents = alloca(list->used*sizeof(*table.contents));
4873 for(nn=0; nn<list->used; nn++)
4875 user = list->list[nn];
4876 if(user->modes & skip_flags)
4882 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4885 nick = alloca(strlen(user->nick)+3);
4886 sprintf(nick, "(%s)", user->nick);
4890 table.contents[table.length][0] = nick;
4893 table_send(chanserv, to->nick, 0, NULL, table);
4896 static CHANSERV_FUNC(cmd_ircops)
4898 reply("CSMSG_STAFF_OPERS");
4899 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4903 static CHANSERV_FUNC(cmd_helpers)
4905 reply("CSMSG_STAFF_HELPERS");
4906 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4910 static CHANSERV_FUNC(cmd_staff)
4912 reply("CSMSG_NETWORK_STAFF");
4913 cmd_ircops(CSFUNC_ARGS);
4914 cmd_helpers(CSFUNC_ARGS);
4918 static CHANSERV_FUNC(cmd_peek)
4920 struct modeNode *mn;
4921 char modes[MODELEN];
4923 struct helpfile_table table;
4924 int opcount = 0, voicecount = 0, srvcount = 0;
4926 irc_make_chanmode(channel, modes);
4928 reply("CSMSG_PEEK_INFO", channel->name);
4929 reply("CSMSG_PEEK_TOPIC", channel->topic);
4930 reply("CSMSG_PEEK_MODES", modes);
4934 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4935 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4936 for(n = 0; n < channel->members.used; n++)
4938 mn = channel->members.list[n];
4939 if(IsLocal(mn->user))
4941 else if(mn->modes & MODE_CHANOP)
4943 else if(mn->modes & MODE_VOICE)
4946 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4948 table.contents[table.length] = alloca(sizeof(**table.contents));
4949 table.contents[table.length][0] = mn->user->nick;
4953 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4954 (channel->members.used - opcount - voicecount - srvcount));
4958 reply("CSMSG_PEEK_OPS");
4959 table_send(chanserv, user->nick, 0, NULL, table);
4962 reply("CSMSG_PEEK_NO_OPS");
4966 static MODCMD_FUNC(cmd_wipeinfo)
4968 struct handle_info *victim;
4969 struct userData *ud, *actor, *real_actor;
4970 unsigned int override = 0;
4973 actor = GetChannelUser(channel->channel_info, user->handle_info);
4974 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4975 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4977 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4979 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4982 if((ud->access >= actor->access) && (ud != actor))
4984 reply("MSG_USER_OUTRANKED", victim->handle);
4987 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4988 override = CMD_LOG_OVERRIDE;
4992 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4993 return 1 | override;
4996 static CHANSERV_FUNC(cmd_resync)
4998 struct mod_chanmode *changes;
4999 struct chanData *cData = channel->channel_info;
5000 unsigned int ii, used;
5002 changes = mod_chanmode_alloc(channel->members.used * 2);
5003 for(ii = used = 0; ii < channel->members.used; ++ii)
5005 struct modeNode *mn = channel->members.list[ii];
5006 struct userData *uData;
5008 if(IsService(mn->user))
5011 uData = GetChannelAccess(cData, mn->user->handle_info);
5012 if(!cData->lvlOpts[lvlGiveOps]
5013 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
5015 if(!(mn->modes & MODE_CHANOP))
5017 if(!uData || IsUserAutoOp(uData))
5019 changes->args[used].mode = MODE_CHANOP;
5020 changes->args[used++].u.member = mn;
5021 if(!(mn->modes & MODE_VOICE))
5023 changes->args[used].mode = MODE_VOICE;
5024 changes->args[used++].u.member = mn;
5029 else if(!cData->lvlOpts[lvlGiveVoice]
5030 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5032 if(mn->modes & MODE_CHANOP)
5034 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5035 changes->args[used++].u.member = mn;
5037 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5039 changes->args[used].mode = MODE_VOICE;
5040 changes->args[used++].u.member = mn;
5047 changes->args[used].mode = MODE_REMOVE | mn->modes;
5048 changes->args[used++].u.member = mn;
5052 changes->argc = used;
5053 modcmd_chanmode_announce(changes);
5054 mod_chanmode_free(changes);
5055 reply("CSMSG_RESYNCED_USERS", channel->name);
5059 static CHANSERV_FUNC(cmd_seen)
5061 struct userData *uData;
5062 struct handle_info *handle;
5063 char seen[INTERVALLEN];
5067 if(!irccasecmp(argv[1], chanserv->nick))
5069 reply("CSMSG_IS_CHANSERV");
5073 if(!(handle = get_handle_info(argv[1])))
5075 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5079 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5081 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5086 reply("CSMSG_USER_PRESENT", handle->handle);
5087 else if(uData->seen)
5088 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5090 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5092 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5093 reply("CSMSG_USER_VACATION", handle->handle);
5098 static MODCMD_FUNC(cmd_names)
5100 struct userNode *targ;
5101 struct userData *targData;
5102 unsigned int ii, pos;
5105 for(ii=pos=0; ii<channel->members.used; ++ii)
5107 targ = channel->members.list[ii]->user;
5108 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5111 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5114 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5118 if(IsUserSuspended(targData))
5120 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5123 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5124 reply("CSMSG_END_NAMES", channel->name);
5129 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5131 switch(ntype->visible_type)
5133 case NOTE_VIS_ALL: return 1;
5134 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5135 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5140 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5142 struct userData *uData;
5144 switch(ntype->set_access_type)
5146 case NOTE_SET_CHANNEL_ACCESS:
5147 if(!user->handle_info)
5149 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5151 return uData->access >= ntype->set_access.min_ulevel;
5152 case NOTE_SET_CHANNEL_SETTER:
5153 return check_user_level(channel, user, lvlSetters, 1, 0);
5154 case NOTE_SET_PRIVILEGED: default:
5155 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5159 static CHANSERV_FUNC(cmd_note)
5161 struct chanData *cData;
5163 struct note_type *ntype;
5165 cData = channel->channel_info;
5168 reply("CSMSG_NOT_REGISTERED", channel->name);
5172 /* If no arguments, show all visible notes for the channel. */
5178 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5180 note = iter_data(it);
5181 if(!note_type_visible_to_user(cData, note->type, user))
5184 reply("CSMSG_NOTELIST_HEADER", channel->name);
5185 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5188 reply("CSMSG_NOTELIST_END", channel->name);
5190 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5192 /* If one argument, show the named note. */
5195 if((note = dict_find(cData->notes, argv[1], NULL))
5196 && note_type_visible_to_user(cData, note->type, user))
5198 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5200 else if((ntype = dict_find(note_types, argv[1], NULL))
5201 && note_type_visible_to_user(NULL, ntype, user))
5203 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5208 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5212 /* Assume they're trying to set a note. */
5216 ntype = dict_find(note_types, argv[1], NULL);
5219 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5222 else if(note_type_settable_by_user(channel, ntype, user))
5224 note_text = unsplit_string(argv+2, argc-2, NULL);
5225 if((note = dict_find(cData->notes, argv[1], NULL)))
5226 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5227 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5228 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5230 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5232 /* The note is viewable to staff only, so return 0
5233 to keep the invocation from getting logged (or
5234 regular users can see it in !events). */
5240 reply("CSMSG_NO_ACCESS");
5247 static CHANSERV_FUNC(cmd_delnote)
5252 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5253 || !note_type_settable_by_user(channel, note->type, user))
5255 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5258 dict_remove(channel->channel_info->notes, note->type->name);
5259 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5263 static CHANSERV_FUNC(cmd_events)
5265 struct logSearch discrim;
5266 struct logReport report;
5267 unsigned int matches, limit;
5269 limit = (argc > 1) ? atoi(argv[1]) : 10;
5270 if(limit < 1 || limit > 200)
5273 memset(&discrim, 0, sizeof(discrim));
5274 discrim.masks.bot = chanserv;
5275 discrim.masks.channel_name = channel->name;
5277 discrim.masks.command = argv[2];
5278 discrim.limit = limit;
5279 discrim.max_time = INT_MAX;
5280 discrim.severities = 1 << LOG_COMMAND;
5281 report.reporter = chanserv;
5283 reply("CSMSG_EVENT_SEARCH_RESULTS");
5284 matches = log_entry_search(&discrim, log_report_entry, &report);
5286 reply("MSG_MATCH_COUNT", matches);
5288 reply("MSG_NO_MATCHES");
5292 static CHANSERV_FUNC(cmd_say)
5298 msg = unsplit_string(argv + 1, argc - 1, NULL);
5299 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5301 else if(*argv[1] == '*' && argv[1][1] != '\0')
5303 struct handle_info *hi;
5304 struct userNode *authed;
5307 msg = unsplit_string(argv + 2, argc - 2, NULL);
5309 if (!(hi = get_handle_info(argv[1] + 1)))
5311 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5315 for (authed = hi->users; authed; authed = authed->next_authed)
5316 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5318 else if(GetUserH(argv[1]))
5321 msg = unsplit_string(argv + 2, argc - 2, NULL);
5322 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5326 reply("MSG_NOT_TARGET_NAME");
5332 static CHANSERV_FUNC(cmd_emote)
5338 /* CTCP is so annoying. */
5339 msg = unsplit_string(argv + 1, argc - 1, NULL);
5340 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5342 else if(*argv[1] == '*' && argv[1][1] != '\0')
5344 struct handle_info *hi;
5345 struct userNode *authed;
5348 msg = unsplit_string(argv + 2, argc - 2, NULL);
5350 if (!(hi = get_handle_info(argv[1] + 1)))
5352 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5356 for (authed = hi->users; authed; authed = authed->next_authed)
5357 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5359 else if(GetUserH(argv[1]))
5361 msg = unsplit_string(argv + 2, argc - 2, NULL);
5362 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5366 reply("MSG_NOT_TARGET_NAME");
5372 struct channelList *
5373 chanserv_support_channels(void)
5375 return &chanserv_conf.support_channels;
5378 static CHANSERV_FUNC(cmd_expire)
5380 int channel_count = registered_channels;
5381 expire_channels(chanserv);
5382 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5387 chanserv_expire_suspension(void *data)
5389 struct suspended *suspended = data;
5390 struct chanNode *channel;
5393 /* Update the channel registration data structure. */
5394 if(!suspended->expires || (now < suspended->expires))
5395 suspended->revoked = now;
5396 channel = suspended->cData->channel;
5397 suspended->cData->channel = channel;
5398 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5400 /* If appropriate, re-join ChanServ to the channel. */
5401 if(!IsOffChannel(suspended->cData))
5403 spamserv_cs_suspend(channel, 0, 0, NULL);
5404 ss_cs_join_channel(channel, 1);
5407 /* Mark everyone currently in the channel as present. */
5408 for(ii = 0; ii < channel->members.used; ++ii)
5410 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5419 static CHANSERV_FUNC(cmd_csuspend)
5421 struct suspended *suspended;
5422 char reason[MAXLEN];
5423 unsigned long expiry, duration;
5424 struct userData *uData;
5428 if(IsProtected(channel->channel_info))
5430 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5434 if(argv[1][0] == '!')
5436 else if(IsSuspended(channel->channel_info))
5438 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5439 show_suspension_info(cmd, user, channel->channel_info->suspended);
5443 if(!strcmp(argv[1], "0"))
5445 else if((duration = ParseInterval(argv[1])))
5446 expiry = now + duration;
5449 reply("MSG_INVALID_DURATION", argv[1]);
5453 unsplit_string(argv + 2, argc - 2, reason);
5455 suspended = calloc(1, sizeof(*suspended));
5456 suspended->revoked = 0;
5457 suspended->issued = now;
5458 suspended->suspender = strdup(user->handle_info->handle);
5459 suspended->expires = expiry;
5460 suspended->reason = strdup(reason);
5461 suspended->cData = channel->channel_info;
5462 suspended->previous = suspended->cData->suspended;
5463 suspended->cData->suspended = suspended;
5465 if(suspended->expires)
5466 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5468 if(IsSuspended(channel->channel_info))
5470 suspended->previous->revoked = now;
5471 if(suspended->previous->expires)
5472 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5473 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5474 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5478 /* Mark all users in channel as absent. */
5479 for(uData = channel->channel_info->users; uData; uData = uData->next)
5488 /* Mark the channel as suspended, then part. */
5489 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5490 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5491 DelChannelUser(chanserv, channel, suspended->reason, 0);
5492 reply("CSMSG_SUSPENDED", channel->name);
5493 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5494 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5499 static CHANSERV_FUNC(cmd_cunsuspend)
5501 struct suspended *suspended;
5502 char message[MAXLEN];
5504 if(!IsSuspended(channel->channel_info))
5506 reply("CSMSG_NOT_SUSPENDED", channel->name);
5510 suspended = channel->channel_info->suspended;
5512 /* Expire the suspension and join ChanServ to the channel. */
5513 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5514 chanserv_expire_suspension(suspended);
5515 reply("CSMSG_UNSUSPENDED", channel->name);
5516 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5517 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5521 typedef struct chanservSearch
5526 unsigned long unvisited;
5527 unsigned long registered;
5529 unsigned long flags;
5533 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5536 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5541 search = malloc(sizeof(struct chanservSearch));
5542 memset(search, 0, sizeof(*search));
5545 for(i = 0; i < argc; i++)
5547 /* Assume all criteria require arguments. */
5550 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5554 if(!irccasecmp(argv[i], "name"))
5555 search->name = argv[++i];
5556 else if(!irccasecmp(argv[i], "registrar"))
5557 search->registrar = argv[++i];
5558 else if(!irccasecmp(argv[i], "unvisited"))
5559 search->unvisited = ParseInterval(argv[++i]);
5560 else if(!irccasecmp(argv[i], "registered"))
5561 search->registered = ParseInterval(argv[++i]);
5562 else if(!irccasecmp(argv[i], "flags"))
5565 if(!irccasecmp(argv[i], "nodelete"))
5566 search->flags |= CHANNEL_NODELETE;
5567 else if(!irccasecmp(argv[i], "suspended"))
5568 search->flags |= CHANNEL_SUSPENDED;
5569 else if(!irccasecmp(argv[i], "unreviewed"))
5570 search->flags |= CHANNEL_UNREVIEWED;
5573 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5577 else if(!irccasecmp(argv[i], "limit"))
5578 search->limit = strtoul(argv[++i], NULL, 10);
5581 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5586 if(search->name && !strcmp(search->name, "*"))
5588 if(search->registrar && !strcmp(search->registrar, "*"))
5589 search->registrar = 0;
5598 chanserv_channel_match(struct chanData *channel, search_t search)
5600 const char *name = channel->channel->name;
5601 if((search->name && !match_ircglob(name, search->name)) ||
5602 (search->registrar && !channel->registrar) ||
5603 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5604 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5605 (search->registered && (now - channel->registered) > search->registered) ||
5606 (search->flags && ((search->flags & channel->flags) != search->flags)))
5613 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5615 struct chanData *channel;
5616 unsigned int matches = 0;
5618 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5620 if(!chanserv_channel_match(channel, search))
5630 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5635 search_print(struct chanData *channel, void *data)
5637 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5640 static CHANSERV_FUNC(cmd_search)
5643 unsigned int matches;
5644 channel_search_func action;
5648 if(!irccasecmp(argv[1], "count"))
5649 action = search_count;
5650 else if(!irccasecmp(argv[1], "print"))
5651 action = search_print;
5654 reply("CSMSG_ACTION_INVALID", argv[1]);
5658 search = chanserv_search_create(user, argc - 2, argv + 2);
5662 if(action == search_count)
5663 search->limit = INT_MAX;
5665 if(action == search_print)
5666 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5668 matches = chanserv_channel_search(search, action, user);
5671 reply("MSG_MATCH_COUNT", matches);
5673 reply("MSG_NO_MATCHES");
5679 static CHANSERV_FUNC(cmd_unvisited)
5681 struct chanData *cData;
5682 unsigned long interval = chanserv_conf.channel_expire_delay;
5683 char buffer[INTERVALLEN];
5684 unsigned int limit = 25, matches = 0;
5688 interval = ParseInterval(argv[1]);
5690 limit = atoi(argv[2]);
5693 intervalString(buffer, interval, user->handle_info);
5694 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5696 for(cData = channelList; cData && matches < limit; cData = cData->next)
5698 if((now - cData->visited) < interval)
5701 intervalString(buffer, now - cData->visited, user->handle_info);
5702 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5709 static MODCMD_FUNC(chan_opt_defaulttopic)
5715 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5717 reply("CSMSG_TOPIC_LOCKED", channel->name);
5721 topic = unsplit_string(argv+1, argc-1, NULL);
5723 free(channel->channel_info->topic);
5724 if(topic[0] == '*' && topic[1] == 0)
5726 topic = channel->channel_info->topic = NULL;
5730 topic = channel->channel_info->topic = strdup(topic);
5731 if(channel->channel_info->topic_mask
5732 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5733 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5735 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5738 if(channel->channel_info->topic)
5739 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5741 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5745 static MODCMD_FUNC(chan_opt_topicmask)
5749 struct chanData *cData = channel->channel_info;
5752 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5754 reply("CSMSG_TOPIC_LOCKED", channel->name);
5758 mask = unsplit_string(argv+1, argc-1, NULL);
5760 if(cData->topic_mask)
5761 free(cData->topic_mask);
5762 if(mask[0] == '*' && mask[1] == 0)
5764 cData->topic_mask = 0;
5768 cData->topic_mask = strdup(mask);
5770 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5771 else if(!match_ircglob(cData->topic, cData->topic_mask))
5772 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5776 if(channel->channel_info->topic_mask)
5777 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5779 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5783 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5787 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5791 if(greeting[0] == '*' && greeting[1] == 0)
5795 unsigned int length = strlen(greeting);
5796 if(length > chanserv_conf.greeting_length)
5798 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5801 *data = strdup(greeting);
5810 reply(name, user_find_message(user, "MSG_NONE"));
5814 static MODCMD_FUNC(chan_opt_greeting)
5816 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5819 static MODCMD_FUNC(chan_opt_usergreeting)
5821 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5824 static MODCMD_FUNC(chan_opt_modes)
5826 struct mod_chanmode *new_modes;
5831 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5833 reply("CSMSG_NO_ACCESS");
5836 if(argv[1][0] == '*' && argv[1][1] == 0)
5838 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5840 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)))
5842 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5845 else if(new_modes->argc > 1)
5847 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5848 mod_chanmode_free(new_modes);
5853 channel->channel_info->modes = *new_modes;
5854 modcmd_chanmode_announce(new_modes);
5855 mod_chanmode_free(new_modes);
5859 mod_chanmode_format(&channel->channel_info->modes, modes);
5861 reply("CSMSG_SET_MODES", modes);
5863 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5867 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5869 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5871 struct chanData *cData = channel->channel_info;
5876 /* Set flag according to value. */
5877 if(enabled_string(argv[1]))
5879 cData->flags |= mask;
5882 else if(disabled_string(argv[1]))
5884 cData->flags &= ~mask;
5889 reply("MSG_INVALID_BINARY", argv[1]);
5895 /* Find current option value. */
5896 value = (cData->flags & mask) ? 1 : 0;
5900 reply(name, user_find_message(user, "MSG_ON"));
5902 reply(name, user_find_message(user, "MSG_OFF"));
5906 static MODCMD_FUNC(chan_opt_nodelete)
5908 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5910 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5914 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5917 static MODCMD_FUNC(chan_opt_dynlimit)
5919 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5922 static MODCMD_FUNC(chan_opt_advtopic)
5924 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5927 static MODCMD_FUNC(chan_opt_offchannel)
5929 struct chanData *cData = channel->channel_info;
5934 /* Set flag according to value. */
5935 if(enabled_string(argv[1]))
5937 if(!IsOffChannel(cData))
5938 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5939 cData->flags |= CHANNEL_OFFCHANNEL;
5942 else if(disabled_string(argv[1]))
5944 if(IsOffChannel(cData))
5946 struct mod_chanmode change;
5947 mod_chanmode_init(&change);
5949 change.args[0].mode = MODE_CHANOP;
5950 change.args[0].u.member = AddChannelUser(chanserv, channel);
5951 mod_chanmode_announce(chanserv, channel, &change);
5953 cData->flags &= ~CHANNEL_OFFCHANNEL;
5958 reply("MSG_INVALID_BINARY", argv[1]);
5964 /* Find current option value. */
5965 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5969 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5971 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5975 static MODCMD_FUNC(chan_opt_unreviewed)
5977 struct chanData *cData = channel->channel_info;
5978 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5984 /* The two directions can have different ACLs. */
5985 if(enabled_string(argv[1]))
5987 else if(disabled_string(argv[1]))
5991 reply("MSG_INVALID_BINARY", argv[1]);
5995 if (new_value != value)
5997 struct svccmd *subcmd;
5998 char subcmd_name[32];
6000 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
6001 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
6004 reply("MSG_COMMAND_DISABLED", subcmd_name);
6007 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6011 cData->flags |= CHANNEL_UNREVIEWED;
6014 free(cData->registrar);
6015 cData->registrar = strdup(user->handle_info->handle);
6016 cData->flags &= ~CHANNEL_UNREVIEWED;
6023 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6025 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6029 static MODCMD_FUNC(chan_opt_defaults)
6031 struct userData *uData;
6032 struct chanData *cData;
6033 const char *confirm;
6034 enum levelOption lvlOpt;
6035 enum charOption chOpt;
6037 cData = channel->channel_info;
6038 uData = GetChannelUser(cData, user->handle_info);
6039 if(!uData || (uData->access < UL_OWNER))
6041 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6044 confirm = make_confirmation_string(uData);
6045 if((argc < 2) || strcmp(argv[1], confirm))
6047 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6050 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6051 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6052 cData->modes = chanserv_conf.default_modes;
6053 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6054 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6055 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6056 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6057 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6062 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6064 struct chanData *cData = channel->channel_info;
6065 struct userData *uData;
6066 unsigned short value;
6070 if(!check_user_level(channel, user, option, 1, 1))
6072 reply("CSMSG_CANNOT_SET");
6075 value = user_level_from_name(argv[1], UL_OWNER+1);
6076 if(!value && strcmp(argv[1], "0"))
6078 reply("CSMSG_INVALID_ACCESS", argv[1]);
6081 uData = GetChannelUser(cData, user->handle_info);
6082 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6084 reply("CSMSG_BAD_SETLEVEL");
6090 if(value > cData->lvlOpts[lvlGiveOps])
6092 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6097 if(value < cData->lvlOpts[lvlGiveVoice])
6099 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6104 /* This test only applies to owners, since non-owners
6105 * trying to set an option to above their level get caught
6106 * by the CSMSG_BAD_SETLEVEL test above.
6108 if(value > uData->access)
6110 reply("CSMSG_BAD_SETTERS");
6117 cData->lvlOpts[option] = value;
6119 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6123 static MODCMD_FUNC(chan_opt_enfops)
6125 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6128 static MODCMD_FUNC(chan_opt_giveops)
6130 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6133 static MODCMD_FUNC(chan_opt_enfmodes)
6135 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6138 static MODCMD_FUNC(chan_opt_enftopic)
6140 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6143 static MODCMD_FUNC(chan_opt_pubcmd)
6145 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6148 static MODCMD_FUNC(chan_opt_setters)
6150 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6153 static MODCMD_FUNC(chan_opt_ctcpusers)
6155 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6158 static MODCMD_FUNC(chan_opt_userinfo)
6160 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6163 static MODCMD_FUNC(chan_opt_givevoice)
6165 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6168 static MODCMD_FUNC(chan_opt_topicsnarf)
6170 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6173 static MODCMD_FUNC(chan_opt_vote)
6175 return channel_level_option(lvlVote, CSFUNC_ARGS);
6178 static MODCMD_FUNC(chan_opt_inviteme)
6180 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6184 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6186 struct chanData *cData = channel->channel_info;
6187 int count = charOptions[option].count, idx;
6191 idx = atoi(argv[1]);
6193 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6195 reply("CSMSG_INVALID_NUMERIC", idx);
6196 /* Show possible values. */
6197 for(idx = 0; idx < count; idx++)
6198 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6202 cData->chOpts[option] = charOptions[option].values[idx].value;
6206 /* Find current option value. */
6209 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6213 /* Somehow, the option value is corrupt; reset it to the default. */
6214 cData->chOpts[option] = charOptions[option].default_value;
6219 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6223 static MODCMD_FUNC(chan_opt_protect)
6225 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6228 static MODCMD_FUNC(chan_opt_toys)
6230 return channel_multiple_option(chToys, CSFUNC_ARGS);
6233 static MODCMD_FUNC(chan_opt_ctcpreaction)
6235 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6238 static MODCMD_FUNC(chan_opt_topicrefresh)
6240 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6243 static struct svccmd_list set_shows_list;
6246 handle_svccmd_unbind(struct svccmd *target) {
6248 for(ii=0; ii<set_shows_list.used; ++ii)
6249 if(target == set_shows_list.list[ii])
6250 set_shows_list.used = 0;
6253 static CHANSERV_FUNC(cmd_set)
6255 struct svccmd *subcmd;
6259 /* Check if we need to (re-)initialize set_shows_list. */
6260 if(!set_shows_list.used)
6262 if(!set_shows_list.size)
6264 set_shows_list.size = chanserv_conf.set_shows->used;
6265 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6267 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6269 const char *name = chanserv_conf.set_shows->list[ii];
6270 sprintf(buf, "%s %s", argv[0], name);
6271 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6274 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6277 svccmd_list_append(&set_shows_list, subcmd);
6283 reply("CSMSG_CHANNEL_OPTIONS");
6284 for(ii = 0; ii < set_shows_list.used; ii++)
6286 subcmd = set_shows_list.list[ii];
6287 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6292 sprintf(buf, "%s %s", argv[0], argv[1]);
6293 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6296 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6299 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6301 reply("CSMSG_NO_ACCESS");
6307 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6311 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6313 struct userData *uData;
6315 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6318 reply("CSMSG_NOT_USER", channel->name);
6324 /* Just show current option value. */
6326 else if(enabled_string(argv[1]))
6328 uData->flags |= mask;
6330 else if(disabled_string(argv[1]))
6332 uData->flags &= ~mask;
6336 reply("MSG_INVALID_BINARY", argv[1]);
6340 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6344 static MODCMD_FUNC(user_opt_noautoop)
6346 struct userData *uData;
6348 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6351 reply("CSMSG_NOT_USER", channel->name);
6354 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6355 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6357 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6360 static MODCMD_FUNC(user_opt_autoinvite)
6362 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6364 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6366 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6369 static MODCMD_FUNC(user_opt_info)
6371 struct userData *uData;
6374 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6378 /* If they got past the command restrictions (which require access)
6379 * but fail this test, we have some fool with security override on.
6381 reply("CSMSG_NOT_USER", channel->name);
6388 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6389 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6391 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6394 bp = strcspn(infoline, "\001");
6397 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6402 if(infoline[0] == '*' && infoline[1] == 0)
6405 uData->info = strdup(infoline);
6408 reply("CSMSG_USET_INFO", uData->info);
6410 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6414 struct svccmd_list uset_shows_list;
6416 static CHANSERV_FUNC(cmd_uset)
6418 struct svccmd *subcmd;
6422 /* Check if we need to (re-)initialize uset_shows_list. */
6423 if(!uset_shows_list.used)
6427 "NoAutoOp", "AutoInvite", "Info"
6430 if(!uset_shows_list.size)
6432 uset_shows_list.size = ArrayLength(options);
6433 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6435 for(ii = 0; ii < ArrayLength(options); ii++)
6437 const char *name = options[ii];
6438 sprintf(buf, "%s %s", argv[0], name);
6439 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6442 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6445 svccmd_list_append(&uset_shows_list, subcmd);
6451 /* Do this so options are presented in a consistent order. */
6452 reply("CSMSG_USER_OPTIONS");
6453 for(ii = 0; ii < uset_shows_list.used; ii++)
6454 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6458 sprintf(buf, "%s %s", argv[0], argv[1]);
6459 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6462 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6466 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6469 static CHANSERV_FUNC(cmd_giveownership)
6471 struct handle_info *new_owner_hi;
6472 struct userData *new_owner;
6473 struct userData *curr_user;
6474 struct userData *invoker;
6475 struct chanData *cData = channel->channel_info;
6476 struct do_not_register *dnr;
6477 const char *confirm;
6478 struct giveownership *giveownership;
6479 unsigned int force, override;
6480 unsigned short co_access, new_owner_old_access;
6481 char reason[MAXLEN], transfer_reason[MAXLEN];
6484 curr_user = GetChannelAccess(cData, user->handle_info);
6485 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6487 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6488 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6489 && (uData->access > 500)
6490 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6491 || uData->access < 500));
6493 if(!curr_user || (curr_user->access != UL_OWNER))
6495 struct userData *owner = NULL;
6496 for(curr_user = channel->channel_info->users;
6498 curr_user = curr_user->next)
6500 if(curr_user->access != UL_OWNER)
6504 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6511 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6513 char delay[INTERVALLEN];
6514 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6515 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6518 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6520 if(new_owner_hi == user->handle_info)
6522 reply("CSMSG_NO_TRANSFER_SELF");
6525 new_owner = GetChannelAccess(cData, new_owner_hi);
6530 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6534 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6538 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6540 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6543 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6544 if(!IsHelping(user))
6545 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6547 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6550 invoker = GetChannelUser(cData, user->handle_info);
6551 if(invoker->access <= UL_OWNER)
6553 confirm = make_confirmation_string(curr_user);
6554 if((argc < 3) || strcmp(argv[2], confirm))
6556 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6560 new_owner_old_access = new_owner->access;
6561 if(new_owner->access >= UL_COOWNER)
6562 co_access = new_owner->access;
6564 co_access = UL_COOWNER;
6565 new_owner->access = UL_OWNER;
6567 curr_user->access = co_access;
6568 cData->ownerTransfer = now;
6569 giveownership = calloc(1, sizeof(*giveownership));
6570 giveownership->issued = now;
6571 giveownership->old_owner = curr_user->handle->handle;
6572 giveownership->target = new_owner_hi->handle;
6573 giveownership->target_access = new_owner_old_access;
6576 if(argc > (2 + force))
6578 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6579 giveownership->reason = strdup(transfer_reason);
6581 giveownership->staff_issuer = strdup(user->handle_info->handle);
6584 giveownership->previous = channel->channel_info->giveownership;
6585 channel->channel_info->giveownership = giveownership;
6586 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6587 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6588 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6593 chanserv_expire_user_suspension(void *data)
6595 struct userData *target = data;
6597 target->expires = 0;
6598 target->flags &= ~USER_SUSPENDED;
6601 static CHANSERV_FUNC(cmd_suspend)
6603 struct handle_info *hi;
6604 struct userData *actor, *real_actor, *target;
6605 unsigned int override = 0;
6609 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6610 actor = GetChannelUser(channel->channel_info, user->handle_info);
6611 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6612 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6614 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6617 if(target->access >= actor->access)
6619 reply("MSG_USER_OUTRANKED", hi->handle);
6622 if(target->flags & USER_SUSPENDED)
6624 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6629 target->present = 0;
6632 if(!real_actor || target->access >= real_actor->access)
6633 override = CMD_LOG_OVERRIDE;
6634 if(!strcmp(argv[2], "0"))
6638 unsigned int duration;
6639 if(!(duration = ParseInterval(argv[2])))
6641 reply("MSG_INVALID_DURATION", argv[2]);
6644 expiry = now + duration;
6647 target->expires = expiry;
6650 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6652 target->flags |= USER_SUSPENDED;
6653 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6654 return 1 | override;
6657 static CHANSERV_FUNC(cmd_unsuspend)
6659 struct handle_info *hi;
6660 struct userData *actor, *real_actor, *target;
6661 unsigned int override = 0;
6664 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6665 actor = GetChannelUser(channel->channel_info, user->handle_info);
6666 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6667 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6669 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6672 if(target->access >= actor->access)
6674 reply("MSG_USER_OUTRANKED", hi->handle);
6677 if(!(target->flags & USER_SUSPENDED))
6679 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6682 if(!real_actor || target->access >= real_actor->access)
6683 override = CMD_LOG_OVERRIDE;
6684 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
6685 target->flags &= ~USER_SUSPENDED;
6686 scan_user_presence(target, NULL);
6687 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6688 return 1 | override;
6691 static MODCMD_FUNC(cmd_deleteme)
6693 struct handle_info *hi;
6694 struct userData *target;
6695 const char *confirm_string;
6696 unsigned short access_level;
6699 hi = user->handle_info;
6700 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6702 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6705 if(target->access == UL_OWNER)
6707 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6710 confirm_string = make_confirmation_string(target);
6711 if((argc < 2) || strcmp(argv[1], confirm_string))
6713 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6716 access_level = target->access;
6717 channel_name = strdup(channel->name);
6718 del_channel_user(target, 1);
6719 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6724 static CHANSERV_FUNC(cmd_addvote)
6726 struct chanData *cData = channel->channel_info;
6727 struct userData *uData, *target;
6728 struct handle_info *hi;
6729 if (!cData) return 0;
6731 hi = user->handle_info;
6732 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6734 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6737 if(target->access < 300) {
6738 reply("CSMSG_NO_ACCESS");
6742 reply("CSMSG_ADDVOTE_FULL");
6746 msg = unsplit_string(argv + 1, argc - 1, NULL);
6747 cData->vote = strdup(msg);
6748 cData->vote_start=0;
6749 dict_delete(cData->vote_options);
6750 cData->vote_options = dict_new();
6751 dict_set_free_data(cData->vote_options, free_vote_options);
6752 for(uData = channel->channel_info->users; uData; uData = uData->next)
6757 reply("CSMSG_ADDVOTE_DONE");
6761 static CHANSERV_FUNC(cmd_delvote)
6763 struct chanData *cData = channel->channel_info;
6764 struct userData *target;
6765 struct handle_info *hi;
6766 if (!cData) return 0;
6767 hi = user->handle_info;
6768 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6770 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6773 if(target->access < 300) {
6774 reply("CSMSG_NO_ACCESS");
6778 reply("CSMSG_NO_VOTE");
6783 reply("CSMSG_DELVOTE_DONE");
6787 static CHANSERV_FUNC(cmd_addoption)
6789 struct chanData *cData = channel->channel_info;
6790 struct userData *target;
6791 struct handle_info *hi;
6792 if (!cData) return 0;
6794 hi = user->handle_info;
6795 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6797 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6800 if(target->access < 300) {
6801 reply("CSMSG_NO_ACCESS");
6805 reply("CSMSG_NO_VOTE");
6811 msg = unsplit_string(argv + 1, argc - 1, NULL);
6814 unsigned int lastid = 1;
6815 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6816 struct vote_option *cvOpt = iter_data(it);
6817 if(cvOpt->option_id > lastid)
6818 lastid = cvOpt->option_id;
6820 struct vote_option *vOpt;
6821 vOpt = calloc(1, sizeof(*vOpt));
6822 vOpt->name = strdup(msg);
6823 vOpt->option_id = (lastid + 1);
6825 sprintf(str,"%i",(lastid + 1));
6826 vOpt->option_str = strdup(str);
6828 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6830 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6834 static CHANSERV_FUNC(cmd_deloption)
6836 struct chanData *cData = channel->channel_info;
6837 struct userData *uData, *target;
6838 struct handle_info *hi;
6839 if (!cData) return 0;
6841 hi = user->handle_info;
6842 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6844 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6847 if(target->access < 300) {
6848 reply("CSMSG_NO_ACCESS");
6852 reply("CSMSG_NO_VOTE");
6855 if(cData->vote_start) {
6856 if(dict_size(cData->vote_options) < 3) {
6857 reply("CSMSG_VOTE_NEED_OPTIONS");
6862 int find_id = atoi(argv[1]);
6864 unsigned int found = 0;
6867 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6869 if (find_id == ii) {
6870 struct vote_option *vOpt = iter_data(it);
6871 found = vOpt->option_id;
6873 sprintf(str,"%i",vOpt->option_id);
6874 dict_remove(cData->vote_options, str);
6879 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6880 if(uData->votefor == found) {
6885 reply("CSMSG_DELOPTION_DONE");
6888 reply("CSMSG_DELOPTION_NONE");
6893 static CHANSERV_FUNC(cmd_vote)
6895 struct chanData *cData = channel->channel_info;
6896 struct userData *target;
6897 struct handle_info *hi;
6898 unsigned int votedfor = 0;
6899 char *votedfor_str = NULL;
6901 if (!cData || !cData->vote) {
6902 reply("CSMSG_NO_VOTE");
6905 if(argc > 1 && cData->vote_start) {
6906 hi = user->handle_info;
6907 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6909 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6912 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6913 reply("CSMSG_NO_ACCESS");
6917 reply("CSMSG_VOTE_VOTED");
6920 int find_id = atoi(argv[1]);
6923 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6925 if (find_id == ii) {
6926 struct vote_option *vOpt = iter_data(it);
6929 target->votefor = vOpt->option_id;
6930 votedfor = vOpt->option_id;
6931 votedfor_str = vOpt->name;
6935 reply("CSMSG_VOTE_INVALID");
6939 if (!cData->vote_start) {
6940 reply("CSMSG_VOTE_NOT_STARTED");
6942 reply("CSMSG_VOTE_QUESTION",cData->vote);
6944 unsigned int voteid = 0;
6947 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6948 struct vote_option *vOpt = iter_data(it);
6950 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6952 if(argc > 1 && cData->vote_start && votedfor_str) {
6953 reply("CSMSG_VOTE_DONE",votedfor_str);
6958 static CHANSERV_FUNC(cmd_startvote)
6960 struct chanData *cData = channel->channel_info;
6961 struct userData *target;
6962 struct handle_info *hi;
6963 if (!cData) return 0;
6964 hi = user->handle_info;
6965 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6967 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6970 if(target->access < 300) {
6971 reply("CSMSG_NO_ACCESS");
6975 reply("CSMSG_NO_VOTE");
6978 if(cData->vote_start) {
6979 reply("CSMSG_STARTVOTE_RUNNING");
6982 if(dict_size(cData->vote_options) < 2) {
6983 reply("CSMSG_VOTE_NEED_OPTIONS");
6986 cData->vote_start = 1;
6987 char response[MAXLEN];
6988 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6989 irc_privmsg(cmd->parent->bot, channel->name, response);
6990 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6991 irc_privmsg(cmd->parent->bot, channel->name, response);
6992 unsigned int voteid = 0;
6994 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6995 struct vote_option *vOpt = iter_data(it);
6997 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6998 irc_privmsg(cmd->parent->bot, channel->name, response);
7000 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
7001 irc_privmsg(cmd->parent->bot, channel->name, response);
7002 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
7003 irc_privmsg(cmd->parent->bot, channel->name, response);
7007 static CHANSERV_FUNC(cmd_endvote)
7009 struct chanData *cData = channel->channel_info;
7010 struct userData *target;
7011 struct handle_info *hi;
7012 if (!cData) return 0;
7013 hi = user->handle_info;
7014 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7016 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7019 if(target->access < 300) {
7020 reply("CSMSG_NO_ACCESS");
7024 reply("CSMSG_NO_VOTE");
7027 if(!cData->vote_start) {
7028 reply("CSMSG_ENDVOTE_STOPPED");
7031 cData->vote_start = 0;
7032 reply("CSMSG_ENDVOTE_DONE");
7036 static CHANSERV_FUNC(cmd_voteresults)
7038 struct chanData *cData = channel->channel_info;
7039 struct userData *target;
7040 struct handle_info *hi;
7041 if (!cData) return 0;
7043 reply("CSMSG_NO_VOTE");
7046 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7047 hi = user->handle_info;
7048 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7050 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7053 if(target->access < 300) {
7054 reply("CSMSG_NO_ACCESS");
7057 char response[MAXLEN];
7058 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7059 irc_privmsg(cmd->parent->bot, channel->name, response);
7060 unsigned int voteid = 0;
7062 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7063 struct vote_option *vOpt = iter_data(it);
7065 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7066 irc_privmsg(cmd->parent->bot, channel->name, response);
7069 reply("CSMSG_VOTE_QUESTION",cData->vote);
7070 unsigned int voteid = 0;
7072 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7073 struct vote_option *vOpt = iter_data(it);
7075 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7082 chanserv_refresh_topics(UNUSED_ARG(void *data))
7084 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7085 struct chanData *cData;
7088 for(cData = channelList; cData; cData = cData->next)
7090 if(IsSuspended(cData))
7092 opt = cData->chOpts[chTopicRefresh];
7095 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7098 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7099 cData->last_refresh = refresh_num;
7101 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7104 static CHANSERV_FUNC(cmd_unf)
7108 char response[MAXLEN];
7109 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7110 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7111 irc_privmsg(cmd->parent->bot, channel->name, response);
7114 reply("CSMSG_UNF_RESPONSE");
7118 static CHANSERV_FUNC(cmd_ping)
7122 char response[MAXLEN];
7123 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7124 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7125 irc_privmsg(cmd->parent->bot, channel->name, response);
7128 reply("CSMSG_PING_RESPONSE");
7132 static CHANSERV_FUNC(cmd_pong)
7136 char response[MAXLEN];
7137 const char *fmt = user_find_message(user, "CSMSG_PONG_RESPONSE");
7138 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7139 irc_privmsg(cmd->parent->bot, channel->name, response);
7142 reply("CSMSG_PONG_RESPONSE");
7146 static CHANSERV_FUNC(cmd_wut)
7150 char response[MAXLEN];
7151 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7152 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7153 irc_privmsg(cmd->parent->bot, channel->name, response);
7156 reply("CSMSG_WUT_RESPONSE");
7160 static CHANSERV_FUNC(cmd_8ball)
7162 unsigned int i, j, accum;
7167 for(i=1; i<argc; i++)
7168 for(j=0; argv[i][j]; j++)
7169 accum = (accum << 5) - accum + toupper(argv[i][j]);
7170 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7173 char response[MAXLEN];
7174 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7175 irc_privmsg(cmd->parent->bot, channel->name, response);
7178 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7182 static CHANSERV_FUNC(cmd_d)
7184 unsigned long sides, count, modifier, ii, total;
7185 char response[MAXLEN], *sep;
7189 if((count = strtoul(argv[1], &sep, 10)) < 1)
7199 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7200 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7204 else if((sep[0] == '-') && isdigit(sep[1]))
7205 modifier = strtoul(sep, NULL, 10);
7206 else if((sep[0] == '+') && isdigit(sep[1]))
7207 modifier = strtoul(sep+1, NULL, 10);
7214 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7219 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7222 for(total = ii = 0; ii < count; ++ii)
7223 total += (rand() % sides) + 1;
7226 if((count > 1) || modifier)
7228 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7229 sprintf(response, fmt, total, count, sides, modifier);
7233 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7234 sprintf(response, fmt, total, sides);
7237 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7239 send_message_type(4, user, cmd->parent->bot, "%s", response);
7243 static CHANSERV_FUNC(cmd_huggle)
7245 /* CTCP must be via PRIVMSG, never notice */
7247 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7249 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7254 chanserv_adjust_limit(void *data)
7256 struct mod_chanmode change;
7257 struct chanData *cData = data;
7258 struct chanNode *channel = cData->channel;
7261 if(IsSuspended(cData))
7264 cData->limitAdjusted = now;
7265 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7266 if(cData->modes.modes_set & MODE_LIMIT)
7268 if(limit > cData->modes.new_limit)
7269 limit = cData->modes.new_limit;
7270 else if(limit == cData->modes.new_limit)
7274 mod_chanmode_init(&change);
7275 change.modes_set = MODE_LIMIT;
7276 change.new_limit = limit;
7277 mod_chanmode_announce(chanserv, channel, &change);
7281 handle_new_channel(struct chanNode *channel)
7283 struct chanData *cData;
7285 if(!(cData = channel->channel_info))
7288 if(cData->modes.modes_set || cData->modes.modes_clear)
7289 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7291 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7292 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7295 void handle_new_channel_created(char *chan, struct userNode *user) {
7296 if(user->handle_info && chanserv_conf.new_channel_authed) {
7297 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7298 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7299 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7301 if(chanserv_conf.new_channel_msg)
7302 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7305 /* Welcome to my worst nightmare. Warning: Read (or modify)
7306 the code below at your own risk. */
7308 handle_join(struct modeNode *mNode)
7310 struct mod_chanmode change;
7311 struct userNode *user = mNode->user;
7312 struct chanNode *channel = mNode->channel;
7313 struct chanData *cData;
7314 struct userData *uData = NULL;
7315 struct banData *bData;
7316 struct handle_info *handle;
7317 unsigned int modes = 0, info = 0;
7321 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7324 cData = channel->channel_info;
7325 if(channel->members.used > cData->max) {
7326 cData->max = channel->members.used;
7327 cData->max_time = now;
7330 for(i = 0; i < channel->invited.used; i++)
7332 if(channel->invited.list[i] == user) {
7333 userList_remove(&channel->invited, user);
7337 /* Check for bans. If they're joining through a ban, one of two
7339 * 1: Join during a netburst, by riding the break. Kick them
7340 * unless they have ops or voice in the channel.
7341 * 2: They're allowed to join through the ban (an invite in
7342 * ircu2.10, or a +e on Hybrid, or something).
7343 * If they're not joining through a ban, and the banlist is not
7344 * full, see if they're on the banlist for the channel. If so,
7347 if(user->uplink->burst && !mNode->modes)
7350 for(ii = 0; ii < channel->banlist.used; ii++)
7352 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7354 /* Riding a netburst. Naughty. */
7355 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7361 mod_chanmode_init(&change);
7363 if(channel->banlist.used < MAXBANS)
7365 /* Not joining through a ban. */
7366 for(bData = cData->bans;
7367 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7368 bData = bData->next);
7372 char kick_reason[MAXLEN];
7373 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7375 bData->triggered = now;
7376 if(bData != cData->bans)
7378 /* Shuffle the ban to the head of the list. */
7380 bData->next->prev = bData->prev;
7382 bData->prev->next = bData->next;
7385 bData->next = cData->bans;
7388 cData->bans->prev = bData;
7389 cData->bans = bData;
7392 change.args[0].mode = MODE_BAN;
7393 change.args[0].u.hostmask = bData->mask;
7394 mod_chanmode_announce(chanserv, channel, &change);
7395 KickChannelUser(user, channel, chanserv, kick_reason);
7400 /* ChanServ will not modify the limits in join-flooded channels,
7401 or when there are enough slots left below the limit. */
7402 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7403 && !channel->join_flooded
7404 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7406 /* The user count has begun "bumping" into the channel limit,
7407 so set a timer to raise the limit a bit. Any previous
7408 timers are removed so three incoming users within the delay
7409 results in one limit change, not three. */
7411 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7412 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7415 if(channel->join_flooded)
7417 /* don't automatically give ops or voice during a join flood */
7419 else if(cData->lvlOpts[lvlGiveOps] == 0)
7420 modes |= MODE_CHANOP;
7421 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7422 modes |= MODE_VOICE;
7424 greeting = cData->greeting;
7425 if(user->handle_info)
7427 handle = user->handle_info;
7429 if(IsHelper(user) && !IsHelping(user))
7432 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7434 if(channel == chanserv_conf.support_channels.list[ii])
7436 HANDLE_SET_FLAG(user->handle_info, HELPING);
7442 uData = GetTrueChannelAccess(cData, handle);
7443 if(uData && !IsUserSuspended(uData))
7445 /* Ops and above were handled by the above case. */
7446 if(IsUserAutoOp(uData))
7448 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7449 modes |= MODE_CHANOP;
7450 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7451 modes |= MODE_VOICE;
7453 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7454 cData->visited = now;
7455 if(cData->user_greeting)
7456 greeting = cData->user_greeting;
7458 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7459 && ((now - uData->seen) >= chanserv_conf.info_delay)
7467 /* If user joining normally (not during burst), apply op or voice,
7468 * and send greeting/userinfo as appropriate.
7470 if(!user->uplink->burst)
7474 if(modes & MODE_CHANOP)
7475 modes &= ~MODE_VOICE;
7476 change.args[0].mode = modes;
7477 change.args[0].u.member = mNode;
7478 mod_chanmode_announce(chanserv, channel, &change);
7481 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7482 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7483 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7489 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7491 struct mod_chanmode change;
7492 struct userData *channel;
7493 unsigned int ii, jj;
7495 if(!user->handle_info)
7498 mod_chanmode_init(&change);
7500 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7502 struct chanNode *cn;
7503 struct modeNode *mn;
7504 if(IsUserSuspended(channel)
7505 || IsSuspended(channel->channel)
7506 || !(cn = channel->channel->channel))
7509 mn = GetUserMode(cn, user);
7512 if(!IsUserSuspended(channel)
7513 && IsUserAutoInvite(channel)
7514 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7516 && !user->uplink->burst)
7517 irc_invite(chanserv, user, cn);
7521 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7522 channel->channel->visited = now;
7524 if(IsUserAutoOp(channel))
7526 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7527 change.args[0].mode = MODE_CHANOP;
7528 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7529 change.args[0].mode = MODE_VOICE;
7531 change.args[0].mode = 0;
7532 change.args[0].u.member = mn;
7533 if(change.args[0].mode)
7534 mod_chanmode_announce(chanserv, cn, &change);
7537 channel->seen = now;
7538 channel->present = 1;
7541 for(ii = 0; ii < user->channels.used; ++ii)
7543 struct chanNode *chan = user->channels.list[ii]->channel;
7544 struct banData *ban;
7546 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7547 || !chan->channel_info
7548 || IsSuspended(chan->channel_info))
7550 for(jj = 0; jj < chan->banlist.used; ++jj)
7551 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7553 if(jj < chan->banlist.used)
7555 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7557 char kick_reason[MAXLEN];
7558 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7560 change.args[0].mode = MODE_BAN;
7561 change.args[0].u.hostmask = ban->mask;
7562 mod_chanmode_announce(chanserv, chan, &change);
7563 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7564 KickChannelUser(user, chan, chanserv, kick_reason);
7565 ban->triggered = now;
7570 if(IsSupportHelper(user))
7572 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7574 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7576 HANDLE_SET_FLAG(user->handle_info, HELPING);
7584 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7586 struct chanData *cData;
7587 struct userData *uData;
7589 cData = mn->channel->channel_info;
7590 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7593 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7595 /* Allow for a bit of padding so that the limit doesn't
7596 track the user count exactly, which could get annoying. */
7597 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7599 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7600 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7604 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7606 scan_user_presence(uData, mn->user);
7608 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7609 cData->visited = now;
7612 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7615 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7616 struct chanNode *channel;
7617 struct userNode *exclude;
7618 /* When looking at the channel that is being /part'ed, we
7619 * have to skip over the client that is leaving. For
7620 * other channels, we must not do that.
7622 channel = chanserv_conf.support_channels.list[ii];
7623 exclude = (channel == mn->channel) ? mn->user : NULL;
7624 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7627 if(ii == chanserv_conf.support_channels.used)
7628 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7633 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7635 struct userData *uData;
7637 if(!channel->channel_info || !kicker || IsService(kicker)
7638 || (kicker == victim) || IsSuspended(channel->channel_info)
7639 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7642 if(protect_user(victim, kicker, channel->channel_info))
7644 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7645 KickChannelUser(kicker, channel, chanserv, reason);
7648 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7653 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7655 struct chanData *cData;
7657 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7660 cData = channel->channel_info;
7661 if(bad_topic(channel, user, channel->topic))
7663 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7664 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7665 SetChannelTopic(channel, chanserv, old_topic, 1);
7666 else if(cData->topic)
7667 SetChannelTopic(channel, chanserv, cData->topic, 1);
7670 /* With topicsnarf, grab the topic and save it as the default topic. */
7671 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7674 cData->topic = strdup(channel->topic);
7680 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7682 struct mod_chanmode *bounce = NULL;
7683 unsigned int bnc, ii;
7686 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7689 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7690 && mode_lock_violated(&channel->channel_info->modes, change))
7692 char correct[MAXLEN];
7693 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7694 mod_chanmode_format(&channel->channel_info->modes, correct);
7695 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7697 for(ii = bnc = 0; ii < change->argc; ++ii)
7699 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7701 const struct userNode *victim = change->args[ii].u.member->user;
7702 if(!protect_user(victim, user, channel->channel_info))
7705 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7708 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7709 bounce->args[bnc].u.member = GetUserMode(channel, user);
7710 if(bounce->args[bnc].u.member)
7714 bounce->args[bnc].mode = MODE_CHANOP;
7715 bounce->args[bnc].u.member = change->args[ii].u.member;
7717 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7719 else if(change->args[ii].mode & MODE_CHANOP)
7721 const struct userNode *victim = change->args[ii].u.member->user;
7722 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7725 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7726 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7727 bounce->args[bnc].u.member = change->args[ii].u.member;
7730 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7732 const char *ban = change->args[ii].u.hostmask;
7733 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7736 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7737 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7738 bounce->args[bnc].u.hostmask = strdup(ban);
7740 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7745 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7746 mod_chanmode_announce(chanserv, channel, bounce);
7747 for(ii = 0; ii < change->argc; ++ii)
7748 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7749 free((char*)bounce->args[ii].u.hostmask);
7750 mod_chanmode_free(bounce);
7755 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7757 struct chanNode *channel;
7758 struct banData *bData;
7759 struct mod_chanmode change;
7760 unsigned int ii, jj;
7761 char kick_reason[MAXLEN];
7763 mod_chanmode_init(&change);
7765 change.args[0].mode = MODE_BAN;
7766 for(ii = 0; ii < user->channels.used; ++ii)
7768 channel = user->channels.list[ii]->channel;
7769 /* Need not check for bans if they're opped or voiced. */
7770 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7772 /* Need not check for bans unless channel registration is active. */
7773 if(!channel->channel_info || IsSuspended(channel->channel_info))
7775 /* Look for a matching ban already on the channel. */
7776 for(jj = 0; jj < channel->banlist.used; ++jj)
7777 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7779 /* Need not act if we found one. */
7780 if(jj < channel->banlist.used)
7782 /* Look for a matching ban in this channel. */
7783 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7785 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7787 change.args[0].u.hostmask = bData->mask;
7788 mod_chanmode_announce(chanserv, channel, &change);
7789 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7790 KickChannelUser(user, channel, chanserv, kick_reason);
7791 bData->triggered = now;
7792 break; /* we don't need to check any more bans in the channel */
7797 static void handle_rename(struct handle_info *handle, const char *old_handle)
7799 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7803 dict_remove2(handle_dnrs, old_handle, 1);
7804 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7805 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7810 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7812 struct userNode *h_user;
7814 if(handle->channels)
7816 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7817 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7819 while(handle->channels)
7820 del_channel_user(handle->channels, 1);
7825 handle_server_link(UNUSED_ARG(struct server *server))
7827 struct chanData *cData;
7829 for(cData = channelList; cData; cData = cData->next)
7831 if(!IsSuspended(cData))
7832 cData->may_opchan = 1;
7833 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7834 && !cData->channel->join_flooded
7835 && ((cData->channel->limit - cData->channel->members.used)
7836 < chanserv_conf.adjust_threshold))
7838 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7839 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7845 chanserv_conf_read(void)
7849 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7850 struct mod_chanmode *change;
7851 struct string_list *strlist;
7852 struct chanNode *chan;
7855 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7857 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7860 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7861 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7862 chanserv_conf.support_channels.used = 0;
7863 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7865 for(ii = 0; ii < strlist->used; ++ii)
7867 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7870 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7872 channelList_append(&chanserv_conf.support_channels, chan);
7875 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7878 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7881 chan = AddChannel(str, now, str2, NULL);
7883 channelList_append(&chanserv_conf.support_channels, chan);
7885 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7886 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7887 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7888 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7889 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7890 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7891 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7892 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7893 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7894 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7895 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7896 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7897 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7898 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7899 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7900 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7901 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7902 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7903 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7904 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7905 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7906 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7907 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7908 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7909 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7910 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7911 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7912 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7913 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7914 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7915 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7917 NickChange(chanserv, str, 0);
7918 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7919 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7920 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7921 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7922 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7923 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7924 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7925 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7926 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7927 chanserv_conf.max_owned = str ? atoi(str) : 5;
7928 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7929 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7930 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7931 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7932 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7933 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7934 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7935 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7936 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7937 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7938 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7939 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7940 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7943 safestrncpy(mode_line, str, sizeof(mode_line));
7944 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7945 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7946 && (change->argc < 2))
7948 chanserv_conf.default_modes = *change;
7949 mod_chanmode_free(change);
7951 free_string_list(chanserv_conf.set_shows);
7952 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7954 strlist = string_list_copy(strlist);
7957 static const char *list[] = {
7958 /* free form text */
7959 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7960 /* options based on user level */
7961 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7962 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7963 /* multiple choice options */
7964 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7965 /* binary options */
7966 "DynLimit", "NoDelete", "expire", "Vote",
7970 strlist = alloc_string_list(ArrayLength(list)-1);
7971 for(ii=0; list[ii]; ii++)
7972 string_list_append(strlist, strdup(list[ii]));
7974 chanserv_conf.set_shows = strlist;
7975 /* We don't look things up now, in case the list refers to options
7976 * defined by modules initialized after this point. Just mark the
7977 * function list as invalid, so it will be initialized.
7979 set_shows_list.used = 0;
7980 free_string_list(chanserv_conf.eightball);
7981 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7984 strlist = string_list_copy(strlist);
7988 strlist = alloc_string_list(4);
7989 string_list_append(strlist, strdup("Yes."));
7990 string_list_append(strlist, strdup("No."));
7991 string_list_append(strlist, strdup("Maybe so."));
7993 chanserv_conf.eightball = strlist;
7994 free_string_list(chanserv_conf.old_ban_names);
7995 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7997 strlist = string_list_copy(strlist);
7999 strlist = alloc_string_list(2);
8000 chanserv_conf.old_ban_names = strlist;
8001 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
8002 off_channel = str ? atoi(str) : 0;
8004 str = database_get_data(conf_node, "oper_channel", RECDB_QSTRING);
8007 chanserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
8011 chanserv_conf.oper_channel = NULL;
8016 chanserv_note_type_read(const char *key, struct record_data *rd)
8019 struct note_type *ntype;
8022 if(!(obj = GET_RECORD_OBJECT(rd)))
8024 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8027 if(!(ntype = chanserv_create_note_type(key)))
8029 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8033 /* Figure out set access */
8034 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8036 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8037 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8039 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8041 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8042 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8044 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8046 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8050 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8051 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8052 ntype->set_access.min_opserv = 0;
8055 /* Figure out visibility */
8056 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8057 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8058 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8059 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8060 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8061 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8062 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8063 ntype->visible_type = NOTE_VIS_ALL;
8065 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8067 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8068 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8072 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8074 struct vote_option *vOpt;
8077 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8079 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8083 vOpt = calloc(1, sizeof(*vOpt));
8084 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8085 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8086 vOpt->voted = str ? atoi(str) : 0;
8087 vOpt->option_id = str ? atoi(key) : 0;
8088 vOpt->option_str = strdup(key);
8089 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8093 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8095 struct handle_info *handle;
8096 struct userData *uData;
8097 char *seen, *inf, *flags, *voted, *votefor, *expires;
8098 unsigned long last_seen;
8099 unsigned short access_level;
8101 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8103 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8107 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8108 if(access_level > UL_OWNER)
8110 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8114 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8115 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8116 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8117 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8118 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8119 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8120 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8121 handle = get_handle_info(key);
8124 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8128 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8129 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8130 uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8132 if((uData->flags & USER_SUSPENDED) && uData->expires)
8134 if(uData->expires > now)
8135 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8137 uData->flags &= ~USER_SUSPENDED;
8140 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8141 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8149 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8151 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8152 unsigned long set_time, triggered_time, expires_time;
8154 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8156 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8160 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8161 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8162 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8163 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8164 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8165 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8166 if (!reason || !owner)
8169 set_time = set ? strtoul(set, NULL, 0) : now;
8170 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8172 expires_time = strtoul(s_expires, NULL, 0);
8174 expires_time = set_time + atoi(s_duration);
8178 if(!reason || (expires_time && (expires_time < now)))
8181 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8184 static struct suspended *
8185 chanserv_read_suspended(dict_t obj)
8187 struct suspended *suspended = calloc(1, sizeof(*suspended));
8191 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8192 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8193 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8194 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8195 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8196 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8197 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8198 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8199 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8200 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8204 static struct giveownership *
8205 chanserv_read_giveownership(dict_t obj)
8207 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8211 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8212 giveownership->staff_issuer = str ? strdup(str) : NULL;
8214 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8216 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8217 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8219 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8220 giveownership->reason = str ? strdup(str) : NULL;
8221 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8222 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8224 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8225 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8226 return giveownership;
8230 chanserv_channel_read(const char *key, struct record_data *hir)
8232 struct suspended *suspended;
8233 struct giveownership *giveownership;
8234 struct mod_chanmode *modes;
8235 struct chanNode *cNode;
8236 struct chanData *cData;
8237 struct dict *channel, *obj;
8238 char *str, *argv[10];
8242 channel = hir->d.object;
8244 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8247 cNode = AddChannel(key, now, NULL, NULL);
8250 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8253 cData = register_channel(cNode, str);
8256 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8260 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8262 enum levelOption lvlOpt;
8263 enum charOption chOpt;
8265 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8266 cData->flags = atoi(str);
8268 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8270 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8272 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8273 else if(levelOptions[lvlOpt].old_flag)
8275 if(cData->flags & levelOptions[lvlOpt].old_flag)
8276 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8278 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8282 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8284 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8286 cData->chOpts[chOpt] = str[0];
8289 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8291 enum levelOption lvlOpt;
8292 enum charOption chOpt;
8295 cData->flags = base64toint(str, 5);
8296 count = strlen(str += 5);
8297 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8300 if(levelOptions[lvlOpt].old_flag)
8302 if(cData->flags & levelOptions[lvlOpt].old_flag)
8303 lvl = levelOptions[lvlOpt].flag_value;
8305 lvl = levelOptions[lvlOpt].default_value;
8307 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8309 case 'c': lvl = UL_COOWNER; break;
8310 case 'm': lvl = UL_MASTER; break;
8311 case 'n': lvl = UL_OWNER+1; break;
8312 case 'o': lvl = UL_OP; break;
8313 case 'p': lvl = UL_PEON; break;
8314 case 'w': lvl = UL_OWNER; break;
8315 default: lvl = 0; break;
8317 cData->lvlOpts[lvlOpt] = lvl;
8319 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8320 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8323 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8325 cData->expiry = atoi(str);
8326 if(cData->expiry > 0) {
8327 if(cData->expiry > now) {
8328 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8330 timeq_add(1, chanserv_expire_channel, cData);
8337 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8339 suspended = chanserv_read_suspended(obj);
8340 cData->suspended = suspended;
8341 suspended->cData = cData;
8342 /* We could use suspended->expires and suspended->revoked to
8343 * set the CHANNEL_SUSPENDED flag, but we don't. */
8345 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8347 suspended = calloc(1, sizeof(*suspended));
8348 suspended->issued = 0;
8349 suspended->revoked = 0;
8350 suspended->suspender = strdup(str);
8351 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8352 suspended->expires = str ? atoi(str) : 0;
8353 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8354 suspended->reason = strdup(str ? str : "No reason");
8355 suspended->previous = NULL;
8356 cData->suspended = suspended;
8357 suspended->cData = cData;
8361 cData->flags &= ~CHANNEL_SUSPENDED;
8362 suspended = NULL; /* to squelch a warning */
8365 if(IsSuspended(cData)) {
8366 if(suspended->expires > now)
8367 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8368 else if(suspended->expires)
8369 cData->flags &= ~CHANNEL_SUSPENDED;
8372 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8374 giveownership = chanserv_read_giveownership(obj);
8375 cData->giveownership = giveownership;
8378 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8379 struct mod_chanmode change;
8380 mod_chanmode_init(&change);
8382 change.args[0].mode = MODE_CHANOP;
8383 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8384 mod_chanmode_announce(chanserv, cNode, &change);
8387 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8388 cData->registered = str ? strtoul(str, NULL, 0) : now;
8389 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8390 cData->visited = str ? strtoul(str, NULL, 0) : now;
8391 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8392 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8393 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8394 cData->max = str ? atoi(str) : 0;
8395 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8396 cData->max_time = str ? atoi(str) : 0;
8397 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8398 cData->greeting = str ? strdup(str) : NULL;
8399 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8400 cData->user_greeting = str ? strdup(str) : NULL;
8401 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8402 cData->topic_mask = str ? strdup(str) : NULL;
8403 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8404 cData->topic = str ? strdup(str) : NULL;
8406 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8408 cData->vote = str ? strdup(str) : NULL;
8409 dict_delete(cData->vote_options);
8410 cData->vote_options = dict_new();
8411 dict_set_free_data(cData->vote_options, free_vote_options);
8412 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8413 cData->vote_start = str ? atoi(str) : 0;
8414 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8415 for(it = dict_first(obj); it; it = iter_next(it)) {
8416 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8420 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8421 for(it = dict_first(obj); it; it = iter_next(it))
8423 struct record_data *rd = iter_data(it);
8424 if(rd->type != RECDB_QSTRING) continue;
8425 int advtopic_index = atoi(iter_key(it));
8426 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8427 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8430 if(!IsSuspended(cData)
8431 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8432 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8433 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8434 cData->modes = *modes;
8436 cData->modes.modes_set |= MODE_REGISTERED;
8437 if(cData->modes.argc > 1)
8438 cData->modes.argc = 1;
8439 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8440 mod_chanmode_free(modes);
8443 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8444 for(it = dict_first(obj); it; it = iter_next(it))
8445 user_read_helper(iter_key(it), iter_data(it), cData);
8447 if(!cData->users && !IsProtected(cData))
8449 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8450 unregister_channel(cData, "has empty user list.");
8454 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8455 for(it = dict_first(obj); it; it = iter_next(it))
8456 ban_read_helper(iter_key(it), iter_data(it), cData);
8458 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8459 for(it = dict_first(obj); it; it = iter_next(it))
8461 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8462 struct record_data *rd = iter_data(it);
8463 const char *note, *setter;
8465 if(rd->type != RECDB_OBJECT)
8467 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8471 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8473 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8475 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8479 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8480 if(!setter) setter = "<unknown>";
8481 chanserv_add_channel_note(cData, ntype, setter, note);
8489 chanserv_dnr_read(const char *key, struct record_data *hir)
8491 const char *setter, *reason, *str;
8492 struct do_not_register *dnr;
8493 unsigned long expiry;
8495 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8498 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8501 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8504 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8507 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8508 expiry = str ? strtoul(str, NULL, 0) : 0;
8509 if(expiry && expiry <= now)
8511 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8514 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8516 dnr->set = atoi(str);
8522 chanserv_saxdb_read(struct dict *database)
8524 struct dict *section;
8527 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8528 for(it = dict_first(section); it; it = iter_next(it))
8529 chanserv_note_type_read(iter_key(it), iter_data(it));
8531 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8532 for(it = dict_first(section); it; it = iter_next(it))
8533 chanserv_channel_read(iter_key(it), iter_data(it));
8535 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8536 for(it = dict_first(section); it; it = iter_next(it))
8537 chanserv_dnr_read(iter_key(it), iter_data(it));
8543 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8545 int high_present = 0;
8546 saxdb_start_record(ctx, KEY_USERS, 1);
8547 for(; uData; uData = uData->next)
8549 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8551 saxdb_start_record(ctx, uData->handle->handle, 0);
8552 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8553 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8555 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8557 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8558 if(uData->channel->vote && uData->voted)
8559 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8560 if(uData->channel->vote && uData->votefor)
8561 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8563 saxdb_write_string(ctx, KEY_INFO, uData->info);
8564 saxdb_end_record(ctx);
8566 saxdb_end_record(ctx);
8567 return high_present;
8571 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8575 saxdb_start_record(ctx, KEY_BANS, 1);
8576 for(; bData; bData = bData->next)
8578 saxdb_start_record(ctx, bData->mask, 0);
8579 saxdb_write_int(ctx, KEY_SET, bData->set);
8580 if(bData->triggered)
8581 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8583 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8585 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8587 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8588 saxdb_end_record(ctx);
8590 saxdb_end_record(ctx);
8594 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8596 saxdb_start_record(ctx, name, 0);
8597 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8598 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8600 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8602 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8604 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8606 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8607 saxdb_end_record(ctx);
8611 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8613 saxdb_start_record(ctx, name, 0);
8614 if(giveownership->staff_issuer)
8615 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8616 if(giveownership->old_owner)
8617 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8618 if(giveownership->target)
8619 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8620 if(giveownership->target_access)
8621 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8622 if(giveownership->reason)
8623 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8624 if(giveownership->issued)
8625 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8626 if(giveownership->previous)
8627 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8628 saxdb_end_record(ctx);
8632 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8636 enum levelOption lvlOpt;
8637 enum charOption chOpt;
8640 saxdb_start_record(ctx, channel->channel->name, 1);
8642 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8643 saxdb_write_int(ctx, KEY_MAX, channel->max);
8644 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8646 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8647 if(channel->registrar)
8648 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8649 if(channel->greeting)
8650 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8651 if(channel->user_greeting)
8652 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8653 if(channel->topic_mask)
8654 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8655 if(channel->suspended)
8656 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8657 if(channel->giveownership)
8658 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8660 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8663 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8664 if(channel->vote_start)
8665 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8666 if (dict_size(channel->vote_options)) {
8667 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8668 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8669 struct vote_option *vOpt = iter_data(it);
8671 sprintf(str,"%i",vOpt->option_id);
8672 saxdb_start_record(ctx, str, 0);
8674 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8676 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8677 saxdb_end_record(ctx);
8679 saxdb_end_record(ctx);
8683 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8684 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8685 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8686 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8687 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8689 buf[0] = channel->chOpts[chOpt];
8691 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8693 saxdb_end_record(ctx);
8695 if(channel->modes.modes_set || channel->modes.modes_clear)
8697 mod_chanmode_format(&channel->modes, buf);
8698 saxdb_write_string(ctx, KEY_MODES, buf);
8701 high_present = chanserv_write_users(ctx, channel->users);
8702 chanserv_write_bans(ctx, channel->bans);
8704 if(channel->flags & CHANNEL_ADVTOPIC) {
8705 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8707 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8708 if(channel->advtopic[advtopic_index])
8709 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8711 saxdb_end_record(ctx);
8714 if(dict_size(channel->notes))
8718 saxdb_start_record(ctx, KEY_NOTES, 1);
8719 for(it = dict_first(channel->notes); it; it = iter_next(it))
8721 struct note *note = iter_data(it);
8722 saxdb_start_record(ctx, iter_key(it), 0);
8723 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8724 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8725 saxdb_end_record(ctx);
8727 saxdb_end_record(ctx);
8730 if(channel->ownerTransfer)
8731 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8732 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8733 saxdb_end_record(ctx);
8737 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8741 saxdb_start_record(ctx, ntype->name, 0);
8742 switch(ntype->set_access_type)
8744 case NOTE_SET_CHANNEL_ACCESS:
8745 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8747 case NOTE_SET_CHANNEL_SETTER:
8748 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8750 case NOTE_SET_PRIVILEGED: default:
8751 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8754 switch(ntype->visible_type)
8756 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8757 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8758 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8760 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8761 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8762 saxdb_end_record(ctx);
8766 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8768 struct do_not_register *dnr;
8769 dict_iterator_t it, next;
8771 for(it = dict_first(dnrs); it; it = next)
8773 next = iter_next(it);
8774 dnr = iter_data(it);
8775 if(dnr->expires && dnr->expires <= now)
8777 dict_remove(dnrs, iter_key(it));
8780 saxdb_start_record(ctx, dnr->chan_name, 0);
8782 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8784 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8785 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8786 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8787 saxdb_end_record(ctx);
8792 chanserv_saxdb_write(struct saxdb_context *ctx)
8795 struct chanData *channel;
8798 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8799 for(it = dict_first(note_types); it; it = iter_next(it))
8800 chanserv_write_note_type(ctx, iter_data(it));
8801 saxdb_end_record(ctx);
8804 saxdb_start_record(ctx, KEY_DNR, 1);
8805 write_dnrs_helper(ctx, handle_dnrs);
8806 write_dnrs_helper(ctx, plain_dnrs);
8807 write_dnrs_helper(ctx, mask_dnrs);
8808 saxdb_end_record(ctx);
8811 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8812 for(channel = channelList; channel; channel = channel->next)
8813 chanserv_write_channel(ctx, channel);
8814 saxdb_end_record(ctx);
8820 chanserv_db_cleanup(void) {
8822 unreg_part_func(handle_part);
8824 unregister_channel(channelList, "terminating.");
8825 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8826 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8827 free(chanserv_conf.support_channels.list);
8828 dict_delete(handle_dnrs);
8829 dict_delete(plain_dnrs);
8830 dict_delete(mask_dnrs);
8831 dict_delete(note_types);
8832 free_string_list(chanserv_conf.eightball);
8833 free_string_list(chanserv_conf.old_ban_names);
8834 free_string_list(chanserv_conf.set_shows);
8835 free(set_shows_list.list);
8836 free(uset_shows_list.list);
8839 struct userData *helper = helperList;
8840 helperList = helperList->next;
8845 #if defined(GCC_VARMACROS)
8846 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8847 #elif defined(C99_VARMACROS)
8848 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8850 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8851 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8854 init_chanserv(const char *nick)
8856 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8857 conf_register_reload(chanserv_conf_read);
8861 reg_server_link_func(handle_server_link);
8862 reg_new_channel_func(handle_new_channel);
8863 reg_join_func(handle_join);
8864 reg_part_func(handle_part);
8865 reg_kick_func(handle_kick);
8866 reg_topic_func(handle_topic);
8867 reg_mode_change_func(handle_mode);
8868 reg_nick_change_func(handle_nick_change);
8869 reg_auth_func(handle_auth);
8872 reg_handle_rename_func(handle_rename);
8873 reg_unreg_func(handle_unreg);
8875 handle_dnrs = dict_new();
8876 dict_set_free_data(handle_dnrs, free);
8877 plain_dnrs = dict_new();
8878 dict_set_free_data(plain_dnrs, free);
8879 mask_dnrs = dict_new();
8880 dict_set_free_data(mask_dnrs, free);
8882 reg_svccmd_unbind_func(handle_svccmd_unbind);
8883 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8884 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8885 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8886 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8887 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8888 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8889 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8890 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8891 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8892 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8893 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8894 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8895 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8897 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8898 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8900 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8901 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8902 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8903 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8904 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8906 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8907 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8908 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8909 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8910 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8912 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8913 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8914 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8915 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8917 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8918 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8919 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8920 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8921 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8922 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8923 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8924 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8926 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8927 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8928 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8929 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8930 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8931 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8932 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8933 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8934 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8935 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8936 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8937 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8938 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8939 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8940 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8942 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8943 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8944 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8945 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8946 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8948 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8949 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8951 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8952 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8953 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8954 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8955 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8956 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8957 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8958 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8959 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8960 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8961 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8963 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8964 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8966 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8967 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8968 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8969 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8971 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8972 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8973 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8974 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8975 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8977 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8978 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8979 DEFINE_COMMAND(pong, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8980 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8981 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8982 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8983 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8985 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8986 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8987 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8988 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8989 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8990 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8991 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8992 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8994 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8996 /* Channel options */
8997 DEFINE_CHANNEL_OPTION(defaulttopic);
8998 DEFINE_CHANNEL_OPTION(topicmask);
8999 DEFINE_CHANNEL_OPTION(greeting);
9000 DEFINE_CHANNEL_OPTION(usergreeting);
9001 DEFINE_CHANNEL_OPTION(modes);
9002 DEFINE_CHANNEL_OPTION(enfops);
9003 DEFINE_CHANNEL_OPTION(giveops);
9004 DEFINE_CHANNEL_OPTION(protect);
9005 DEFINE_CHANNEL_OPTION(enfmodes);
9006 DEFINE_CHANNEL_OPTION(enftopic);
9007 DEFINE_CHANNEL_OPTION(pubcmd);
9008 DEFINE_CHANNEL_OPTION(givevoice);
9009 DEFINE_CHANNEL_OPTION(userinfo);
9010 DEFINE_CHANNEL_OPTION(dynlimit);
9011 DEFINE_CHANNEL_OPTION(topicsnarf);
9012 DEFINE_CHANNEL_OPTION(vote);
9013 DEFINE_CHANNEL_OPTION(nodelete);
9014 DEFINE_CHANNEL_OPTION(toys);
9015 DEFINE_CHANNEL_OPTION(setters);
9016 DEFINE_CHANNEL_OPTION(topicrefresh);
9017 DEFINE_CHANNEL_OPTION(ctcpusers);
9018 DEFINE_CHANNEL_OPTION(ctcpreaction);
9019 DEFINE_CHANNEL_OPTION(inviteme);
9020 DEFINE_CHANNEL_OPTION(advtopic);
9021 DEFINE_CHANNEL_OPTION(unreviewed);
9022 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
9023 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9024 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9026 DEFINE_CHANNEL_OPTION(offchannel);
9027 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9029 /* Alias set topic to set defaulttopic for compatibility. */
9030 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9033 DEFINE_USER_OPTION(noautoop);
9034 DEFINE_USER_OPTION(autoinvite);
9035 DEFINE_USER_OPTION(info);
9037 /* Alias uset autovoice to uset autoop. */
9038 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9040 note_types = dict_new();
9041 dict_set_free_data(note_types, chanserv_deref_note_type);
9044 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9045 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9046 service_register(chanserv)->trigger = '!';
9047 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9049 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9051 if(chanserv_conf.channel_expire_frequency)
9052 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9054 if(chanserv_conf.dnr_expire_frequency)
9055 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9057 if(chanserv_conf.refresh_period)
9059 unsigned long next_refresh;
9060 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9061 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9064 reg_exit_func(chanserv_db_cleanup);
9065 message_register_table(msgtab);