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_WUT_RESPONSE", "wut" },
487 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
488 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
489 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
490 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
491 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
492 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
493 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
496 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
497 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
498 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
499 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
500 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
501 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
502 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
503 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
504 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
505 { "CSMSG_VOTE_QUESTION", "Question: %s" },
506 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
507 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
508 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
509 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
510 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
511 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
512 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
513 { "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." },
514 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
515 { "CSMSG_VOTE_VOTED", "You have already voted." },
516 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
517 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
518 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
519 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
522 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
526 #define CSMSG_ALERT_REGISTERED "%s registered to %s by %s."
527 #define CSMSG_ALERT_UNREGISTERED "%s %s"
529 /* eject_user and unban_user flags */
530 #define ACTION_KICK 0x0001
531 #define ACTION_BAN 0x0002
532 #define ACTION_ADD_BAN 0x0004
533 #define ACTION_ADD_TIMED_BAN 0x0008
534 #define ACTION_UNBAN 0x0010
535 #define ACTION_DEL_BAN 0x0020
537 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
538 #define MODELEN 40 + KEYLEN
542 #define CSFUNC_ARGS user, channel, argc, argv, cmd
544 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
545 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
546 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
547 reply("MSG_MISSING_PARAMS", argv[0]); \
551 DECLARE_LIST(dnrList, struct do_not_register *);
552 DEFINE_LIST(dnrList, struct do_not_register *)
554 #define chanserv_notice(target, format...) send_message(target , chanserv , ## format)
555 #define chanserv_oper_message(format...) do { if(chanserv_conf.oper_channel) send_channel_message(chanserv_conf.oper_channel , chanserv , ## format); } while(0)
557 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
559 struct userNode *chanserv;
562 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
563 static struct log_type *CS_LOG;
567 struct channelList support_channels;
568 struct mod_chanmode default_modes;
570 unsigned long db_backup_frequency;
571 unsigned long channel_expire_frequency;
572 unsigned long dnr_expire_frequency;
574 unsigned long invited_timeout;
576 unsigned long info_delay;
577 unsigned long adjust_delay;
578 unsigned long channel_expire_delay;
579 unsigned int nodelete_level;
581 unsigned int adjust_threshold;
582 int join_flood_threshold;
584 unsigned int greeting_length;
585 unsigned int refresh_period;
586 unsigned int giveownership_period;
588 unsigned int max_owned;
589 unsigned int max_chan_users;
590 unsigned int max_chan_bans;
591 unsigned int min_time_bans;
592 unsigned int max_userinfo_length;
594 unsigned int revoke_mode_a;
596 struct string_list *set_shows;
597 struct string_list *eightball;
598 struct string_list *old_ban_names;
600 const char *ctcp_short_ban_duration;
601 const char *ctcp_long_ban_duration;
603 const char *irc_operator_epithet;
604 const char *network_helper_epithet;
605 const char *support_helper_epithet;
607 const char *new_channel_authed;
608 const char *new_channel_unauthed;
609 const char *new_channel_msg;
611 struct chanNode *oper_channel;
616 struct userNode *user;
617 struct userNode *bot;
618 struct chanNode *channel;
620 unsigned short lowest;
621 unsigned short highest;
622 struct userData **users;
623 struct helpfile_table table;
628 struct userNode *user;
629 struct chanNode *chan;
632 enum note_access_type
634 NOTE_SET_CHANNEL_ACCESS,
635 NOTE_SET_CHANNEL_SETTER,
639 enum note_visible_type
642 NOTE_VIS_CHANNEL_USERS,
648 enum note_access_type set_access_type;
650 unsigned int min_opserv;
651 unsigned short min_ulevel;
653 enum note_visible_type visible_type;
654 unsigned int max_length;
661 struct note_type *type;
662 char setter[NICKSERV_HANDLE_LEN+1];
666 static unsigned int registered_channels;
667 static unsigned int banCount;
669 static const struct {
672 unsigned short level;
675 { "peon", "Peon", UL_PEON, '+' },
676 { "op", "Op", UL_OP, '@' },
677 { "master", "Master", UL_MASTER, '%' },
678 { "coowner", "Coowner", UL_COOWNER, '*' },
679 { "owner", "Owner", UL_OWNER, '!' },
680 { "helper", "BUG:", UL_HELPER, 'X' }
683 static const struct {
686 unsigned short default_value;
687 unsigned int old_idx;
688 unsigned int old_flag;
689 unsigned short flag_value;
691 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
692 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
693 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
694 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
695 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
696 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
697 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
698 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
699 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
700 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
701 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
702 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
705 struct charOptionValues {
708 } protectValues[] = {
709 { 'a', "CSMSG_PROTECT_ALL" },
710 { 'e', "CSMSG_PROTECT_EQUAL" },
711 { 'l', "CSMSG_PROTECT_LOWER" },
712 { 'n', "CSMSG_PROTECT_NONE" }
714 { 'd', "CSMSG_TOYS_DISABLED" },
715 { 'n', "CSMSG_TOYS_PRIVATE" },
716 { 'p', "CSMSG_TOYS_PUBLIC" }
717 }, topicRefreshValues[] = {
718 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
719 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
720 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
721 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
722 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
723 }, ctcpReactionValues[] = {
724 { 'k', "CSMSG_CTCPREACTION_KICK" },
725 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
726 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
727 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
730 static const struct {
734 unsigned int old_idx;
736 struct charOptionValues *values;
738 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
739 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
740 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
741 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
744 struct userData *helperList;
745 struct chanData *channelList;
746 static struct module *chanserv_module;
747 static unsigned int userCount;
749 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
750 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
751 static void unregister_channel(struct chanData *channel, const char *reason);
754 user_level_from_name(const char *name, unsigned short clamp_level)
756 unsigned int level = 0, ii;
758 level = strtoul(name, NULL, 10);
759 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
760 if(!irccasecmp(name, accessLevels[ii].name))
761 level = accessLevels[ii].level;
762 if(level > clamp_level)
768 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
771 *minl = strtoul(arg, &sep, 10);
779 *maxl = strtoul(sep+1, &sep, 10);
787 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
789 struct userData *uData, **head;
791 if(!channel || !handle)
794 if(override && HANDLE_FLAGGED(handle, HELPING)
795 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
797 for(uData = helperList;
798 uData && uData->handle != handle;
799 uData = uData->next);
803 uData = calloc(1, sizeof(struct userData));
804 uData->handle = handle;
806 uData->access = UL_HELPER;
812 uData->next = helperList;
814 helperList->prev = uData;
822 for(uData = channel->users; uData; uData = uData->next)
823 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
826 head = &(channel->users);
829 if(uData && (uData != *head))
831 /* Shuffle the user to the head of whatever list he was in. */
833 uData->next->prev = uData->prev;
835 uData->prev->next = uData->next;
841 (**head).prev = uData;
848 /* Returns non-zero if user has at least the minimum access.
849 * exempt_owner is set when handling !set, so the owner can set things
852 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
854 struct userData *uData;
855 struct chanData *cData = channel->channel_info;
856 unsigned short minimum = cData->lvlOpts[opt];
859 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
862 if(minimum <= uData->access)
864 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
869 /* Scan for other users authenticated to the same handle
870 still in the channel. If so, keep them listed as present.
872 user is optional, if not null, it skips checking that userNode
873 (for the handle_part function) */
875 scan_user_presence(struct userData *uData, struct userNode *user)
879 if(IsSuspended(uData->channel)
880 || IsUserSuspended(uData)
881 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
893 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
895 unsigned int eflags, argc;
897 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
899 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
900 if(!channel->channel_info
901 || IsSuspended(channel->channel_info)
903 || !ircncasecmp(text, "ACTION ", 7))
905 /* Figure out the minimum level needed to CTCP the channel */
906 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
908 /* We need to enforce against them; do so. */
910 argv[0] = (char*)text;
911 argv[1] = user->nick;
913 if(GetUserMode(channel, user))
914 eflags |= ACTION_KICK;
915 switch(channel->channel_info->chOpts[chCTCPReaction]) {
916 default: case 'k': /* just do the kick */ break;
918 eflags |= ACTION_BAN;
921 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
922 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
925 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
926 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
929 argv[argc++] = bad_ctcp_reason;
930 eject_user(chanserv, channel, argc, argv, NULL, eflags);
934 chanserv_create_note_type(const char *name)
936 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
937 strcpy(ntype->name, name);
939 dict_insert(note_types, ntype->name, ntype);
944 free_vote_options(void *data)
946 struct vote_option *vOpt = data;
948 free(vOpt->option_str);
953 chanserv_deref_note_type(void *data)
955 struct note_type *ntype = data;
957 if(--ntype->refs > 0)
963 chanserv_flush_note_type(struct note_type *ntype)
965 struct chanData *cData;
966 for(cData = channelList; cData; cData = cData->next)
967 dict_remove(cData->notes, ntype->name);
971 chanserv_truncate_notes(struct note_type *ntype)
973 struct chanData *cData;
975 unsigned int size = sizeof(*note) + ntype->max_length;
977 for(cData = channelList; cData; cData = cData->next) {
978 note = dict_find(cData->notes, ntype->name, NULL);
981 if(strlen(note->note) <= ntype->max_length)
983 dict_remove2(cData->notes, ntype->name, 1);
984 note = realloc(note, size);
985 note->note[ntype->max_length] = 0;
986 dict_insert(cData->notes, ntype->name, note);
990 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
993 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
996 unsigned int len = strlen(text);
998 if(len > type->max_length) len = type->max_length;
999 note = calloc(1, sizeof(*note) + len);
1001 strncpy(note->setter, setter, sizeof(note->setter)-1);
1002 memcpy(note->note, text, len);
1003 note->note[len] = 0;
1004 dict_insert(channel->notes, type->name, note);
1010 chanserv_free_note(void *data)
1012 struct note *note = data;
1014 chanserv_deref_note_type(note->type);
1015 assert(note->type->refs > 0); /* must use delnote to remove the type */
1019 static MODCMD_FUNC(cmd_createnote) {
1020 struct note_type *ntype;
1021 unsigned int arg = 1, existed = 0, max_length;
1023 if((ntype = dict_find(note_types, argv[1], NULL)))
1026 ntype = chanserv_create_note_type(argv[arg]);
1027 if(!irccasecmp(argv[++arg], "privileged"))
1030 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1031 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1033 else if(!irccasecmp(argv[arg], "channel"))
1035 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1038 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1041 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1042 ntype->set_access.min_ulevel = ulvl;
1044 else if(!irccasecmp(argv[arg], "setter"))
1046 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1050 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1054 if(!irccasecmp(argv[++arg], "privileged"))
1055 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1056 else if(!irccasecmp(argv[arg], "channel_users"))
1057 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1058 else if(!irccasecmp(argv[arg], "all"))
1059 ntype->visible_type = NOTE_VIS_ALL;
1061 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1065 if((arg+1) >= argc) {
1066 reply("MSG_MISSING_PARAMS", argv[0]);
1069 max_length = strtoul(argv[++arg], NULL, 0);
1070 if(max_length < 20 || max_length > 450)
1072 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1075 if(existed && (max_length < ntype->max_length))
1077 ntype->max_length = max_length;
1078 chanserv_truncate_notes(ntype);
1080 ntype->max_length = max_length;
1083 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1085 reply("CSMSG_NOTE_CREATED", ntype->name);
1090 dict_remove(note_types, ntype->name);
1094 static MODCMD_FUNC(cmd_removenote) {
1095 struct note_type *ntype;
1098 ntype = dict_find(note_types, argv[1], NULL);
1099 force = (argc > 2) && !irccasecmp(argv[2], "force");
1102 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1109 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1112 chanserv_flush_note_type(ntype);
1114 dict_remove(note_types, argv[1]);
1115 reply("CSMSG_NOTE_DELETED", argv[1]);
1120 chanserv_expire_channel(void *data)
1122 struct chanData *channel = data;
1123 char reason[MAXLEN];
1124 sprintf(reason, "channel expired.");
1125 channel->expiry = 0;
1126 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1127 unregister_channel(channel, reason);
1130 static MODCMD_FUNC(chan_opt_expire)
1132 struct chanData *cData = channel->channel_info;
1133 unsigned long value = cData->expiry;
1137 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1139 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1142 unsigned long expiry,duration;
1144 /* The two directions can have different ACLs. */
1145 if(!strcmp(argv[1], "0"))
1147 else if((duration = ParseInterval(argv[1])))
1148 expiry = now + duration;
1151 reply("MSG_INVALID_DURATION", argv[1]);
1155 if (expiry != value)
1159 timeq_del(value, chanserv_expire_channel, cData, 0);
1162 cData->expiry = value;
1165 timeq_add(expiry, chanserv_expire_channel, cData);
1170 if(cData->expiry > now) {
1171 char expirestr[INTERVALLEN];
1172 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1174 reply("CSMSG_SET_EXPIRE_OFF");
1179 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1183 if(orig->modes_set & change->modes_clear)
1185 if(orig->modes_clear & change->modes_set)
1187 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1188 && strcmp(orig->new_key, change->new_key))
1190 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1191 && (orig->new_limit != change->new_limit))
1196 static char max_length_text[MAXLEN+1][16];
1198 static struct helpfile_expansion
1199 chanserv_expand_variable(const char *variable)
1201 struct helpfile_expansion exp;
1203 if(!irccasecmp(variable, "notes"))
1206 exp.type = HF_TABLE;
1207 exp.value.table.length = 1;
1208 exp.value.table.width = 3;
1209 exp.value.table.flags = 0;
1210 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1211 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1212 exp.value.table.contents[0][0] = "Note Type";
1213 exp.value.table.contents[0][1] = "Visibility";
1214 exp.value.table.contents[0][2] = "Max Length";
1215 for(it=dict_first(note_types); it; it=iter_next(it))
1217 struct note_type *ntype = iter_data(it);
1220 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1221 row = exp.value.table.length++;
1222 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1223 exp.value.table.contents[row][0] = ntype->name;
1224 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1225 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1227 if(!max_length_text[ntype->max_length][0])
1228 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1229 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1234 exp.type = HF_STRING;
1235 exp.value.str = NULL;
1239 static struct chanData*
1240 register_channel(struct chanNode *cNode, char *registrar)
1242 struct chanData *channel;
1243 enum levelOption lvlOpt;
1244 enum charOption chOpt;
1247 channel = calloc(1, sizeof(struct chanData));
1249 channel->notes = dict_new();
1250 dict_set_free_data(channel->notes, chanserv_free_note);
1252 channel->registrar = strdup(registrar);
1253 channel->registered = now;
1254 channel->visited = now;
1255 channel->limitAdjusted = now;
1256 channel->ownerTransfer = now;
1257 channel->flags = CHANNEL_DEFAULT_FLAGS;
1258 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1259 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1260 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1261 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1262 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1263 channel->advtopic[i] = NULL;
1265 channel->prev = NULL;
1266 channel->next = channelList;
1269 channelList->prev = channel;
1270 channelList = channel;
1271 registered_channels++;
1273 channel->channel = cNode;
1275 cNode->channel_info = channel;
1277 channel->vote = NULL;
1282 static struct userData*
1283 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1285 struct userData *ud;
1287 if(access_level > UL_OWNER)
1290 ud = calloc(1, sizeof(*ud));
1291 ud->channel = channel;
1292 ud->handle = handle;
1294 ud->access = access_level;
1295 ud->info = info ? strdup(info) : NULL;
1298 ud->next = channel->users;
1300 channel->users->prev = ud;
1301 channel->users = ud;
1303 channel->userCount++;
1307 ud->u_next = ud->handle->channels;
1309 ud->u_next->u_prev = ud;
1310 ud->handle->channels = ud;
1316 del_channel_user(struct userData *user, int do_gc)
1318 struct chanData *channel = user->channel;
1320 channel->userCount--;
1324 user->prev->next = user->next;
1326 channel->users = user->next;
1328 user->next->prev = user->prev;
1331 user->u_prev->u_next = user->u_next;
1333 user->handle->channels = user->u_next;
1335 user->u_next->u_prev = user->u_prev;
1339 if(do_gc && !channel->users && !IsProtected(channel)) {
1340 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1341 unregister_channel(channel, "lost all users.");
1345 static void expire_ban(void *data);
1348 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1351 unsigned int ii, l1, l2;
1356 bd = malloc(sizeof(struct banData));
1358 bd->channel = channel;
1360 bd->triggered = triggered;
1361 bd->expires = expires;
1363 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1365 extern const char *hidden_host_suffix;
1366 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1370 l2 = strlen(old_name);
1373 if(irccasecmp(mask + l1 - l2, old_name))
1375 new_mask = alloca(MAXLEN);
1376 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1379 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1381 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1382 bd->reason = strdup(reason);
1385 timeq_add(expires, expire_ban, bd);
1388 bd->next = channel->bans;
1390 channel->bans->prev = bd;
1392 channel->banCount++;
1399 del_channel_ban(struct banData *ban)
1401 ban->channel->banCount--;
1405 ban->prev->next = ban->next;
1407 ban->channel->bans = ban->next;
1410 ban->next->prev = ban->prev;
1413 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1422 expire_ban(void *data)
1424 struct banData *bd = data;
1425 if(!IsSuspended(bd->channel))
1427 struct banList bans;
1428 struct mod_chanmode change;
1430 bans = bd->channel->channel->banlist;
1431 mod_chanmode_init(&change);
1432 for(ii=0; ii<bans.used; ii++)
1434 if(!strcmp(bans.list[ii]->ban, bd->mask))
1437 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1438 change.args[0].u.hostmask = bd->mask;
1439 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1445 del_channel_ban(bd);
1448 static void chanserv_expire_suspension(void *data);
1451 unregister_channel(struct chanData *channel, const char *reason)
1453 struct mod_chanmode change;
1454 char msgbuf[MAXLEN];
1457 /* After channel unregistration, the following must be cleaned
1459 - Channel information.
1462 - Channel suspension data.
1463 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1469 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1471 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1472 mod_chanmode_init(&change);
1474 change.modes_clear |= MODE_REGISTERED;
1475 if(chanserv_conf.revoke_mode_a)
1476 change.modes_clear |= MODE_ACCESS;
1477 mod_chanmode_announce(chanserv, channel->channel, &change);
1480 while(channel->users)
1481 del_channel_user(channel->users, 0);
1483 while(channel->bans)
1484 del_channel_ban(channel->bans);
1486 free(channel->topic);
1487 free(channel->registrar);
1488 free(channel->greeting);
1489 free(channel->user_greeting);
1490 free(channel->topic_mask);
1492 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1493 if(channel->advtopic[i])
1494 free(channel->advtopic[i]);
1498 channel->prev->next = channel->next;
1500 channelList = channel->next;
1503 channel->next->prev = channel->prev;
1505 if(channel->suspended)
1507 struct chanNode *cNode = channel->channel;
1508 struct suspended *suspended, *next_suspended;
1510 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1512 next_suspended = suspended->previous;
1513 free(suspended->suspender);
1514 free(suspended->reason);
1515 if(suspended->expires)
1516 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1521 cNode->channel_info = NULL;
1524 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1525 channel->channel->channel_info = NULL;
1526 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1527 dict_delete(channel->notes);
1528 if(!IsSuspended(channel))
1529 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1531 chanserv_oper_message(CSMSG_ALERT_UNREGISTERED, channel->channel->name, reason);
1532 UnlockChannel(channel->channel);
1534 registered_channels--;
1538 expire_channels(void *data)
1540 struct chanData *channel, *next;
1541 struct userData *user;
1542 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1544 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1545 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1547 for(channel = channelList; channel; channel = next)
1549 next = channel->next;
1551 /* See if the channel can be expired. */
1552 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1553 || IsProtected(channel))
1556 /* Make sure there are no high-ranking users still in the channel. */
1557 for(user=channel->users; user; user=user->next)
1558 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1563 /* Unregister the channel */
1564 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1565 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1566 unregister_channel(channel, "registration expired.");
1569 if(chanserv_conf.channel_expire_frequency && !data)
1570 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1574 expire_dnrs(UNUSED_ARG(void *data))
1576 dict_iterator_t it, next;
1577 struct do_not_register *dnr;
1579 for(it = dict_first(handle_dnrs); it; it = next)
1581 dnr = iter_data(it);
1582 next = iter_next(it);
1583 if(dnr->expires && dnr->expires <= now)
1584 dict_remove(handle_dnrs, dnr->chan_name + 1);
1586 for(it = dict_first(plain_dnrs); it; it = next)
1588 dnr = iter_data(it);
1589 next = iter_next(it);
1590 if(dnr->expires && dnr->expires <= now)
1591 dict_remove(plain_dnrs, dnr->chan_name + 1);
1593 for(it = dict_first(mask_dnrs); it; it = next)
1595 dnr = iter_data(it);
1596 next = iter_next(it);
1597 if(dnr->expires && dnr->expires <= now)
1598 dict_remove(mask_dnrs, dnr->chan_name + 1);
1601 if(chanserv_conf.dnr_expire_frequency)
1602 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1606 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1608 char protect = channel->chOpts[chProtect];
1609 struct userData *cs_victim, *cs_aggressor;
1611 /* Don't protect if no one is to be protected, someone is attacking
1612 himself, or if the aggressor is an IRC Operator. */
1613 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1616 /* Don't protect if the victim isn't authenticated (because they
1617 can't be a channel user), unless we are to protect non-users
1619 cs_victim = GetChannelAccess(channel, victim->handle_info);
1620 if(protect != 'a' && !cs_victim)
1623 /* Protect if the aggressor isn't a user because at this point,
1624 the aggressor can only be less than or equal to the victim. */
1625 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1629 /* If the aggressor was a user, then the victim can't be helped. */
1636 if(cs_victim->access > cs_aggressor->access)
1641 if(cs_victim->access >= cs_aggressor->access)
1650 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1652 struct chanData *cData = channel->channel_info;
1653 struct userData *cs_victim;
1655 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1656 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1657 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1659 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1667 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1669 if(IsService(victim))
1671 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1675 if(protect_user(victim, user, channel->channel_info))
1677 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1684 static struct do_not_register *
1685 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1687 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1688 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1689 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1690 strcpy(dnr->reason, reason);
1692 dnr->expires = expires;
1693 if(dnr->chan_name[0] == '*')
1694 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1695 else if(strpbrk(dnr->chan_name, "*?"))
1696 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1698 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1702 static struct dnrList
1703 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1705 struct dnrList list;
1706 dict_iterator_t it, next;
1707 struct do_not_register *dnr;
1709 dnrList_init(&list);
1711 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1713 if(dnr->expires && dnr->expires <= now)
1714 dict_remove(handle_dnrs, handle);
1715 else if(list.used < max)
1716 dnrList_append(&list, dnr);
1719 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1721 if(dnr->expires && dnr->expires <= now)
1722 dict_remove(plain_dnrs, chan_name);
1723 else if(list.used < max)
1724 dnrList_append(&list, dnr);
1729 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1731 next = iter_next(it);
1732 if(!match_ircglob(chan_name, iter_key(it)))
1734 dnr = iter_data(it);
1735 if(dnr->expires && dnr->expires <= now)
1736 dict_remove(mask_dnrs, iter_key(it));
1738 dnrList_append(&list, dnr);
1745 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1747 struct userNode *user;
1748 char buf1[INTERVALLEN];
1749 char buf2[INTERVALLEN];
1756 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1761 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1762 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1766 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1769 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1774 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1776 struct dnrList list;
1779 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1780 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1781 dnr_print_func(list.list[ii], user);
1783 reply("CSMSG_MORE_DNRS", list.used - ii);
1788 struct do_not_register *
1789 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1791 struct dnrList list;
1792 struct do_not_register *dnr;
1794 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1795 dnr = list.used ? list.list[0] : NULL;
1800 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1802 struct do_not_register *dnr;
1803 dict_iterator_t it, next;
1804 unsigned int matches = 0;
1806 for(it = dict_first(dict); it; it = next)
1808 dnr = iter_data(it);
1809 next = iter_next(it);
1810 if(dnr->expires && dnr->expires <= now)
1812 dict_remove(dict, iter_key(it));
1815 dnr_print_func(dnr, user);
1822 static CHANSERV_FUNC(cmd_noregister)
1826 unsigned long expiry, duration;
1827 unsigned int matches;
1831 reply("CSMSG_DNR_SEARCH_RESULTS");
1832 matches = send_dnrs(user, handle_dnrs);
1833 matches += send_dnrs(user, plain_dnrs);
1834 matches += send_dnrs(user, mask_dnrs);
1836 reply("MSG_MATCH_COUNT", matches);
1838 reply("MSG_NO_MATCHES");
1844 if(!IsChannelName(target) && (*target != '*'))
1846 reply("CSMSG_NOT_DNR", target);
1854 reply("MSG_INVALID_DURATION", argv[2]);
1858 if(!strcmp(argv[2], "0"))
1860 else if((duration = ParseInterval(argv[2])))
1861 expiry = now + duration;
1864 reply("MSG_INVALID_DURATION", argv[2]);
1868 reason = unsplit_string(argv + 3, argc - 3, NULL);
1869 if((*target == '*') && !get_handle_info(target + 1))
1871 reply("MSG_HANDLE_UNKNOWN", target + 1);
1874 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1875 reply("CSMSG_NOREGISTER_CHANNEL", target);
1879 reply("CSMSG_DNR_SEARCH_RESULTS");
1881 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1883 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1885 reply("MSG_NO_MATCHES");
1889 static CHANSERV_FUNC(cmd_allowregister)
1891 const char *chan_name = argv[1];
1893 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1894 || dict_remove(plain_dnrs, chan_name)
1895 || dict_remove(mask_dnrs, chan_name))
1897 reply("CSMSG_DNR_REMOVED", chan_name);
1900 reply("CSMSG_NO_SUCH_DNR", chan_name);
1905 struct userNode *source;
1909 unsigned long min_set, max_set;
1910 unsigned long min_expires, max_expires;
1915 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1917 return !((dnr->set < search->min_set)
1918 || (dnr->set > search->max_set)
1919 || (dnr->expires < search->min_expires)
1920 || (search->max_expires
1921 && ((dnr->expires == 0)
1922 || (dnr->expires > search->max_expires)))
1923 || (search->chan_mask
1924 && !match_ircglob(dnr->chan_name, search->chan_mask))
1925 || (search->setter_mask
1926 && !match_ircglob(dnr->setter, search->setter_mask))
1927 || (search->reason_mask
1928 && !match_ircglob(dnr->reason, search->reason_mask)));
1931 static struct dnr_search *
1932 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1934 struct dnr_search *discrim;
1937 discrim = calloc(1, sizeof(*discrim));
1938 discrim->source = user;
1939 discrim->chan_mask = NULL;
1940 discrim->setter_mask = NULL;
1941 discrim->reason_mask = NULL;
1942 discrim->max_set = INT_MAX;
1943 discrim->limit = 50;
1945 for(ii=0; ii<argc; ++ii)
1949 reply("MSG_MISSING_PARAMS", argv[ii]);
1952 else if(0 == irccasecmp(argv[ii], "channel"))
1954 discrim->chan_mask = argv[++ii];
1956 else if(0 == irccasecmp(argv[ii], "setter"))
1958 discrim->setter_mask = argv[++ii];
1960 else if(0 == irccasecmp(argv[ii], "reason"))
1962 discrim->reason_mask = argv[++ii];
1964 else if(0 == irccasecmp(argv[ii], "limit"))
1966 discrim->limit = strtoul(argv[++ii], NULL, 0);
1968 else if(0 == irccasecmp(argv[ii], "set"))
1970 const char *cmp = argv[++ii];
1973 discrim->min_set = now - ParseInterval(cmp + 2);
1975 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1976 } else if(cmp[0] == '=') {
1977 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1978 } else if(cmp[0] == '>') {
1980 discrim->max_set = now - ParseInterval(cmp + 2);
1982 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1984 discrim->max_set = now - (ParseInterval(cmp) - 1);
1987 else if(0 == irccasecmp(argv[ii], "expires"))
1989 const char *cmp = argv[++ii];
1992 discrim->max_expires = now + ParseInterval(cmp + 2);
1994 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1995 } else if(cmp[0] == '=') {
1996 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1997 } else if(cmp[0] == '>') {
1999 discrim->min_expires = now + ParseInterval(cmp + 2);
2001 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2003 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2008 reply("MSG_INVALID_CRITERIA", argv[ii]);
2019 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2022 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2024 struct do_not_register *dnr;
2025 dict_iterator_t next;
2030 /* Initialize local variables. */
2033 if(discrim->chan_mask)
2035 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2036 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2040 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2042 /* Check against account-based DNRs. */
2043 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2044 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2047 else if(target_fixed)
2049 /* Check against channel-based DNRs. */
2050 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2051 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2056 /* Exhaustively search account DNRs. */
2057 for(it = dict_first(handle_dnrs); it; it = next)
2059 next = iter_next(it);
2060 dnr = iter_data(it);
2061 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2065 /* Do the same for channel DNRs. */
2066 for(it = dict_first(plain_dnrs); it; it = next)
2068 next = iter_next(it);
2069 dnr = iter_data(it);
2070 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2074 /* Do the same for wildcarded channel DNRs. */
2075 for(it = dict_first(mask_dnrs); it; it = next)
2077 next = iter_next(it);
2078 dnr = iter_data(it);
2079 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2087 dnr_remove_func(struct do_not_register *match, void *extra)
2089 struct userNode *user;
2092 chan_name = alloca(strlen(match->chan_name) + 1);
2093 strcpy(chan_name, match->chan_name);
2095 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2096 || dict_remove(plain_dnrs, chan_name)
2097 || dict_remove(mask_dnrs, chan_name))
2099 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2105 dnr_count_func(struct do_not_register *match, void *extra)
2107 return 0; (void)match; (void)extra;
2110 static MODCMD_FUNC(cmd_dnrsearch)
2112 struct dnr_search *discrim;
2113 dnr_search_func action;
2114 struct svccmd *subcmd;
2115 unsigned int matches;
2118 sprintf(buf, "dnrsearch %s", argv[1]);
2119 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2122 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2125 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2127 if(!irccasecmp(argv[1], "print"))
2128 action = dnr_print_func;
2129 else if(!irccasecmp(argv[1], "remove"))
2130 action = dnr_remove_func;
2131 else if(!irccasecmp(argv[1], "count"))
2132 action = dnr_count_func;
2135 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2139 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2143 if(action == dnr_print_func)
2144 reply("CSMSG_DNR_SEARCH_RESULTS");
2145 matches = dnr_search(discrim, action, user);
2147 reply("MSG_MATCH_COUNT", matches);
2149 reply("MSG_NO_MATCHES");
2155 chanserv_get_owned_count(struct handle_info *hi)
2157 struct userData *cList;
2160 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2161 if(cList->access == UL_OWNER)
2166 static CHANSERV_FUNC(cmd_register)
2168 struct handle_info *handle;
2169 struct chanData *cData;
2170 struct modeNode *mn;
2172 unsigned int new_channel, force=0;
2173 struct do_not_register *dnr;
2177 if(channel->channel_info)
2179 reply("CSMSG_ALREADY_REGGED", channel->name);
2183 if(channel->bad_channel)
2185 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2190 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2192 reply("CSMSG_MUST_BE_OPPED", channel->name);
2197 chan_name = channel->name;
2201 if((argc < 2) || !IsChannelName(argv[1]))
2203 reply("MSG_NOT_CHANNEL_NAME");
2207 if(opserv_bad_channel(argv[1]))
2209 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2214 chan_name = argv[1];
2217 if(argc >= (new_channel+2))
2219 if(!IsHelping(user))
2221 reply("CSMSG_PROXY_FORBIDDEN");
2225 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2227 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2228 dnr = chanserv_is_dnr(chan_name, handle);
2232 handle = user->handle_info;
2233 dnr = chanserv_is_dnr(chan_name, handle);
2237 if(!IsHelping(user))
2238 reply("CSMSG_DNR_CHANNEL", chan_name);
2240 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2244 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2246 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2251 channel = AddChannel(argv[1], now, NULL, NULL);
2253 cData = register_channel(channel, user->handle_info->handle);
2254 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2255 cData->modes = chanserv_conf.default_modes;
2257 cData->modes.modes_set |= MODE_REGISTERED;
2258 if (IsOffChannel(cData))
2260 mod_chanmode_announce(chanserv, channel, &cData->modes);
2264 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2265 change->args[change->argc].mode = MODE_CHANOP;
2266 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2268 mod_chanmode_announce(chanserv, channel, change);
2269 mod_chanmode_free(change);
2272 /* Initialize the channel's max user record. */
2273 cData->max = channel->members.used;
2274 cData->max_time = 0;
2276 if(handle != user->handle_info)
2277 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2279 reply("CSMSG_REG_SUCCESS", channel->name);
2281 chanserv_oper_message(CSMSG_ALERT_REGISTERED, channel->name, handle->handle, user->handle_info->handle);
2286 make_confirmation_string(struct userData *uData)
2288 static char strbuf[16];
2293 for(src = uData->handle->handle; *src; )
2294 accum = accum * 31 + toupper(*src++);
2296 for(src = uData->channel->channel->name; *src; )
2297 accum = accum * 31 + toupper(*src++);
2298 sprintf(strbuf, "%08x", accum);
2302 static CHANSERV_FUNC(cmd_unregister)
2305 char reason[MAXLEN];
2306 struct chanData *cData;
2307 struct userData *uData;
2309 cData = channel->channel_info;
2312 reply("CSMSG_NOT_REGISTERED", channel->name);
2316 uData = GetChannelUser(cData, user->handle_info);
2317 if(!uData || (uData->access < UL_OWNER))
2319 reply("CSMSG_NO_ACCESS");
2323 if(IsProtected(cData) && !IsOper(user))
2325 reply("CSMSG_UNREG_NODELETE", channel->name);
2329 if(!IsHelping(user))
2331 const char *confirm_string;
2332 if(IsSuspended(cData))
2334 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2337 confirm_string = make_confirmation_string(uData);
2338 if((argc < 2) || strcmp(argv[1], confirm_string))
2340 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2345 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2346 name = strdup(channel->name);
2347 unregister_channel(cData, reason);
2348 spamserv_cs_unregister(user, channel, manually, "unregistered");
2349 reply("CSMSG_UNREG_SUCCESS", name);
2355 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2357 extern struct userNode *spamserv;
2358 struct mod_chanmode *change;
2360 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2362 change = mod_chanmode_alloc(2);
2364 change->args[0].mode = MODE_CHANOP;
2365 change->args[0].u.member = AddChannelUser(chanserv, channel);
2366 change->args[1].mode = MODE_CHANOP;
2367 change->args[1].u.member = AddChannelUser(spamserv, channel);
2371 change = mod_chanmode_alloc(1);
2373 change->args[0].mode = MODE_CHANOP;
2374 change->args[0].u.member = AddChannelUser(chanserv, channel);
2377 mod_chanmode_announce(chanserv, channel, change);
2378 mod_chanmode_free(change);
2381 static CHANSERV_FUNC(cmd_move)
2383 struct mod_chanmode change;
2384 struct chanNode *target;
2385 struct modeNode *mn;
2386 struct userData *uData;
2387 char reason[MAXLEN];
2388 struct do_not_register *dnr;
2389 int chanserv_join = 0, spamserv_join;
2393 if(IsProtected(channel->channel_info))
2395 reply("CSMSG_MOVE_NODELETE", channel->name);
2399 if(!IsChannelName(argv[1]))
2401 reply("MSG_NOT_CHANNEL_NAME");
2405 if(opserv_bad_channel(argv[1]))
2407 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2411 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2413 for(uData = channel->channel_info->users; uData; uData = uData->next)
2415 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2417 if(!IsHelping(user))
2418 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2420 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2426 mod_chanmode_init(&change);
2427 if(!(target = GetChannel(argv[1])))
2429 target = AddChannel(argv[1], now, NULL, NULL);
2430 if(!IsSuspended(channel->channel_info))
2433 else if(target->channel_info)
2435 reply("CSMSG_ALREADY_REGGED", target->name);
2438 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2439 && !IsHelping(user))
2441 reply("CSMSG_MUST_BE_OPPED", target->name);
2444 else if(!IsSuspended(channel->channel_info))
2449 /* Clear MODE_REGISTERED from old channel, add it to new. */
2451 change.modes_clear = MODE_REGISTERED;
2452 mod_chanmode_announce(chanserv, channel, &change);
2453 change.modes_clear = 0;
2454 change.modes_set = MODE_REGISTERED;
2455 mod_chanmode_announce(chanserv, target, &change);
2458 /* Move the channel_info to the target channel; it
2459 shouldn't be necessary to clear timeq callbacks
2460 for the old channel. */
2461 target->channel_info = channel->channel_info;
2462 target->channel_info->channel = target;
2463 channel->channel_info = NULL;
2465 /* Check whether users are present in the new channel. */
2466 for(uData = target->channel_info->users; uData; uData = uData->next)
2467 scan_user_presence(uData, NULL);
2469 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2472 ss_cs_join_channel(target, spamserv_join);
2474 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2475 if(!IsSuspended(target->channel_info))
2477 char reason2[MAXLEN];
2478 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2479 DelChannelUser(chanserv, channel, reason2, 0);
2481 UnlockChannel(channel);
2482 LockChannel(target);
2483 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2484 reply("CSMSG_MOVE_SUCCESS", target->name);
2489 merge_users(struct chanData *source, struct chanData *target)
2491 struct userData *suData, *tuData, *next;
2497 /* Insert the source's users into the scratch area. */
2498 for(suData = source->users; suData; suData = suData->next)
2499 dict_insert(merge, suData->handle->handle, suData);
2501 /* Iterate through the target's users, looking for
2502 users common to both channels. The lower access is
2503 removed from either the scratch area or target user
2505 for(tuData = target->users; tuData; tuData = next)
2507 struct userData *choice;
2509 next = tuData->next;
2511 /* If a source user exists with the same handle as a target
2512 channel's user, resolve the conflict by removing one. */
2513 suData = dict_find(merge, tuData->handle->handle, NULL);
2517 /* Pick the data we want to keep. */
2518 /* If the access is the same, use the later seen time. */
2519 if(suData->access == tuData->access)
2520 choice = (suData->seen > tuData->seen) ? suData : tuData;
2521 else /* Otherwise, keep the higher access level. */
2522 choice = (suData->access > tuData->access) ? suData : tuData;
2523 /* Use the later seen time. */
2524 if(suData->seen < tuData->seen)
2525 suData->seen = tuData->seen;
2527 tuData->seen = suData->seen;
2529 /* Remove the user that wasn't picked. */
2530 if(choice == tuData)
2532 dict_remove(merge, suData->handle->handle);
2533 del_channel_user(suData, 0);
2536 del_channel_user(tuData, 0);
2539 /* Move the remaining users to the target channel. */
2540 for(it = dict_first(merge); it; it = iter_next(it))
2542 suData = iter_data(it);
2544 /* Insert the user into the target channel's linked list. */
2545 suData->prev = NULL;
2546 suData->next = target->users;
2547 suData->channel = target;
2550 target->users->prev = suData;
2551 target->users = suData;
2553 /* Update the user counts for the target channel; the
2554 source counts are left alone. */
2555 target->userCount++;
2557 /* Check whether the user is in the target channel. */
2558 scan_user_presence(suData, NULL);
2561 /* Possible to assert (source->users == NULL) here. */
2562 source->users = NULL;
2567 merge_bans(struct chanData *source, struct chanData *target)
2569 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2571 /* Hold on to the original head of the target ban list
2572 to avoid comparing source bans with source bans. */
2573 tFront = target->bans;
2575 /* Perform a totally expensive O(n*m) merge, ick. */
2576 for(sbData = source->bans; sbData; sbData = sNext)
2578 /* Flag to track whether the ban's been moved
2579 to the destination yet. */
2582 /* Possible to assert (sbData->prev == NULL) here. */
2583 sNext = sbData->next;
2585 for(tbData = tFront; tbData; tbData = tNext)
2587 tNext = tbData->next;
2589 /* Perform two comparisons between each source
2590 and target ban, conflicts are resolved by
2591 keeping the broader ban and copying the later
2592 expiration and triggered time. */
2593 if(match_ircglobs(tbData->mask, sbData->mask))
2595 /* There is a broader ban in the target channel that
2596 overrides one in the source channel; remove the
2597 source ban and break. */
2598 if(sbData->expires > tbData->expires)
2599 tbData->expires = sbData->expires;
2600 if(sbData->triggered > tbData->triggered)
2601 tbData->triggered = sbData->triggered;
2602 del_channel_ban(sbData);
2605 else if(match_ircglobs(sbData->mask, tbData->mask))
2607 /* There is a broader ban in the source channel that
2608 overrides one in the target channel; remove the
2609 target ban, fall through and move the source over. */
2610 if(tbData->expires > sbData->expires)
2611 sbData->expires = tbData->expires;
2612 if(tbData->triggered > sbData->triggered)
2613 sbData->triggered = tbData->triggered;
2614 if(tbData == tFront)
2616 del_channel_ban(tbData);
2619 /* Source bans can override multiple target bans, so
2620 we allow a source to run through this loop multiple
2621 times, but we can only move it once. */
2626 /* Remove the source ban from the source ban list. */
2628 sbData->next->prev = sbData->prev;
2630 /* Modify the source ban's associated channel. */
2631 sbData->channel = target;
2633 /* Insert the ban into the target channel's linked list. */
2634 sbData->prev = NULL;
2635 sbData->next = target->bans;
2638 target->bans->prev = sbData;
2639 target->bans = sbData;
2641 /* Update the user counts for the target channel. */
2646 /* Possible to assert (source->bans == NULL) here. */
2647 source->bans = NULL;
2651 merge_data(struct chanData *source, struct chanData *target)
2653 /* Use more recent visited and owner-transfer time; use older
2654 * registered time. Bitwise or may_opchan. Use higher max.
2655 * Do not touch last_refresh, ban count or user counts.
2657 if(source->visited > target->visited)
2658 target->visited = source->visited;
2659 if(source->registered < target->registered)
2660 target->registered = source->registered;
2661 if(source->ownerTransfer > target->ownerTransfer)
2662 target->ownerTransfer = source->ownerTransfer;
2663 if(source->may_opchan)
2664 target->may_opchan = 1;
2665 if(source->max > target->max) {
2666 target->max = source->max;
2667 target->max_time = source->max_time;
2672 merge_channel(struct chanData *source, struct chanData *target)
2674 merge_users(source, target);
2675 merge_bans(source, target);
2676 merge_data(source, target);
2679 static CHANSERV_FUNC(cmd_merge)
2681 struct userData *target_user;
2682 struct chanNode *target;
2683 char reason[MAXLEN];
2687 /* Make sure the target channel exists and is registered to the user
2688 performing the command. */
2689 if(!(target = GetChannel(argv[1])))
2691 reply("MSG_INVALID_CHANNEL");
2695 if(!target->channel_info)
2697 reply("CSMSG_NOT_REGISTERED", target->name);
2701 if(IsProtected(channel->channel_info))
2703 reply("CSMSG_MERGE_NODELETE");
2707 if(IsSuspended(target->channel_info))
2709 reply("CSMSG_MERGE_SUSPENDED");
2713 if(channel == target)
2715 reply("CSMSG_MERGE_SELF");
2719 target_user = GetChannelUser(target->channel_info, user->handle_info);
2720 if(!target_user || (target_user->access < UL_OWNER))
2722 reply("CSMSG_MERGE_NOT_OWNER");
2726 /* Merge the channel structures and associated data. */
2727 merge_channel(channel->channel_info, target->channel_info);
2728 spamserv_cs_move_merge(user, channel, target, 0);
2729 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2730 unregister_channel(channel->channel_info, reason);
2731 reply("CSMSG_MERGE_SUCCESS", target->name);
2735 static CHANSERV_FUNC(cmd_opchan)
2737 struct mod_chanmode change;
2738 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2740 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2743 channel->channel_info->may_opchan = 0;
2744 mod_chanmode_init(&change);
2746 change.args[0].mode = MODE_CHANOP;
2747 change.args[0].u.member = GetUserMode(channel, chanserv);
2748 if(!change.args[0].u.member)
2750 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2753 mod_chanmode_announce(chanserv, channel, &change);
2754 reply("CSMSG_OPCHAN_DONE", channel->name);
2758 static CHANSERV_FUNC(cmd_adduser)
2760 struct userData *actee;
2761 struct userData *actor, *real_actor;
2762 struct handle_info *handle;
2763 unsigned short access_level, override = 0;
2767 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2769 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2773 access_level = user_level_from_name(argv[2], UL_OWNER);
2776 reply("CSMSG_INVALID_ACCESS", argv[2]);
2780 actor = GetChannelUser(channel->channel_info, user->handle_info);
2781 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2783 if(actor->access <= access_level)
2785 reply("CSMSG_NO_BUMP_ACCESS");
2789 /* Trying to add someone with equal/more access? */
2790 if (!real_actor || real_actor->access <= access_level)
2791 override = CMD_LOG_OVERRIDE;
2793 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2796 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2798 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2802 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2803 scan_user_presence(actee, NULL);
2804 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2805 return 1 | override;
2808 static CHANSERV_FUNC(cmd_clvl)
2810 struct handle_info *handle;
2811 struct userData *victim;
2812 struct userData *actor, *real_actor;
2813 unsigned short new_access, override = 0;
2814 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2818 actor = GetChannelUser(channel->channel_info, user->handle_info);
2819 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2821 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2824 if(handle == user->handle_info && !privileged)
2826 reply("CSMSG_NO_SELF_CLVL");
2830 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2832 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2836 if(actor->access <= victim->access && !privileged)
2838 reply("MSG_USER_OUTRANKED", handle->handle);
2842 new_access = user_level_from_name(argv[2], UL_OWNER);
2846 reply("CSMSG_INVALID_ACCESS", argv[2]);
2850 if(new_access >= actor->access && !privileged)
2852 reply("CSMSG_NO_BUMP_ACCESS");
2856 /* Trying to clvl a equal/higher user? */
2857 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2858 override = CMD_LOG_OVERRIDE;
2859 /* Trying to clvl someone to equal/higher access? */
2860 if(!real_actor || new_access >= real_actor->access)
2861 override = CMD_LOG_OVERRIDE;
2862 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2863 * If they lower their own access it's not a big problem.
2866 victim->access = new_access;
2867 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2868 return 1 | override;
2871 static CHANSERV_FUNC(cmd_deluser)
2873 struct handle_info *handle;
2874 struct userData *victim;
2875 struct userData *actor, *real_actor;
2876 unsigned short access_level, override = 0;
2881 actor = GetChannelUser(channel->channel_info, user->handle_info);
2882 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2884 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2887 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2889 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2895 access_level = user_level_from_name(argv[1], UL_OWNER);
2898 reply("CSMSG_INVALID_ACCESS", argv[1]);
2901 if(access_level != victim->access)
2903 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2909 access_level = victim->access;
2912 if((actor->access <= victim->access) && !IsHelping(user))
2914 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2918 /* If people delete themselves it is an override, but they
2919 * could've used deleteme so we don't log it as an override
2921 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2922 override = CMD_LOG_OVERRIDE;
2924 chan_name = strdup(channel->name);
2925 del_channel_user(victim, 1);
2926 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2928 return 1 | override;
2932 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2934 struct userData *actor, *real_actor, *uData, *next;
2935 unsigned int override = 0;
2937 actor = GetChannelUser(channel->channel_info, user->handle_info);
2938 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2940 if(min_access > max_access)
2942 reply("CSMSG_BAD_RANGE", min_access, max_access);
2946 if(actor->access <= max_access)
2948 reply("CSMSG_NO_ACCESS");
2952 if(!real_actor || real_actor->access <= max_access)
2953 override = CMD_LOG_OVERRIDE;
2955 for(uData = channel->channel_info->users; uData; uData = next)
2959 if((uData->access >= min_access)
2960 && (uData->access <= max_access)
2961 && match_ircglob(uData->handle->handle, mask))
2962 del_channel_user(uData, 1);
2965 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2966 return 1 | override;
2969 static CHANSERV_FUNC(cmd_mdelowner)
2971 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2974 static CHANSERV_FUNC(cmd_mdelcoowner)
2976 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2979 static CHANSERV_FUNC(cmd_mdelmaster)
2981 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2984 static CHANSERV_FUNC(cmd_mdelop)
2986 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2989 static CHANSERV_FUNC(cmd_mdelpeon)
2991 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2995 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2997 struct banData *bData, *next;
2998 char interval[INTERVALLEN];
3000 unsigned long limit;
3003 limit = now - duration;
3004 for(bData = channel->channel_info->bans; bData; bData = next)
3008 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3011 del_channel_ban(bData);
3015 intervalString(interval, duration, user->handle_info);
3016 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3021 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3023 struct userData *actor, *uData, *next;
3024 char interval[INTERVALLEN];
3026 unsigned long limit;
3028 actor = GetChannelUser(channel->channel_info, user->handle_info);
3029 if(min_access > max_access)
3031 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3035 if(!actor || actor->access <= max_access)
3037 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3042 limit = now - duration;
3043 for(uData = channel->channel_info->users; uData; uData = next)
3047 if((uData->seen > limit)
3049 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3052 if(((uData->access >= min_access) && (uData->access <= max_access))
3053 || (!max_access && (uData->access < actor->access)))
3055 del_channel_user(uData, 1);
3063 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3065 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3069 static CHANSERV_FUNC(cmd_trim)
3071 unsigned long duration;
3072 unsigned short min_level, max_level;
3077 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3078 duration = ParseInterval(argv[2]);
3081 reply("CSMSG_CANNOT_TRIM");
3085 if(!irccasecmp(argv[1], "bans"))
3087 cmd_trim_bans(user, channel, duration);
3090 else if(!irccasecmp(argv[1], "users"))
3092 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3095 else if(parse_level_range(&min_level, &max_level, argv[1]))
3097 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3100 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3102 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3107 reply("CSMSG_INVALID_TRIM", argv[1]);
3112 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3113 to the user. cmd_all takes advantage of this. */
3114 static CHANSERV_FUNC(cmd_up)
3116 struct mod_chanmode change;
3117 struct userData *uData;
3120 mod_chanmode_init(&change);
3122 change.args[0].u.member = GetUserMode(channel, user);
3123 if(!change.args[0].u.member)
3126 reply("MSG_CHANNEL_ABSENT", channel->name);
3130 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3134 reply("CSMSG_GODMODE_UP", argv[0]);
3137 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3139 change.args[0].mode = MODE_CHANOP;
3140 errmsg = "CSMSG_ALREADY_OPPED";
3142 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3144 change.args[0].mode = MODE_VOICE;
3145 errmsg = "CSMSG_ALREADY_VOICED";
3150 reply("CSMSG_NO_ACCESS");
3153 change.args[0].mode &= ~change.args[0].u.member->modes;
3154 if(!change.args[0].mode)
3157 reply(errmsg, channel->name);
3160 modcmd_chanmode_announce(&change);
3164 static CHANSERV_FUNC(cmd_down)
3166 struct mod_chanmode change;
3168 mod_chanmode_init(&change);
3170 change.args[0].u.member = GetUserMode(channel, user);
3171 if(!change.args[0].u.member)
3174 reply("MSG_CHANNEL_ABSENT", channel->name);
3178 if(!change.args[0].u.member->modes)
3181 reply("CSMSG_ALREADY_DOWN", channel->name);
3185 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3186 modcmd_chanmode_announce(&change);
3190 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)
3192 struct userData *cList;
3194 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3196 if(IsSuspended(cList->channel)
3197 || IsUserSuspended(cList)
3198 || !GetUserMode(cList->channel->channel, user))
3201 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3207 static CHANSERV_FUNC(cmd_upall)
3209 return cmd_all(CSFUNC_ARGS, cmd_up);
3212 static CHANSERV_FUNC(cmd_downall)
3214 return cmd_all(CSFUNC_ARGS, cmd_down);
3217 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3218 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3221 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)
3223 unsigned int ii, valid;
3224 struct userNode *victim;
3225 struct mod_chanmode *change;
3227 change = mod_chanmode_alloc(argc - 1);
3229 for(ii=valid=0; ++ii < argc; )
3231 if(!(victim = GetUserH(argv[ii])))
3233 change->args[valid].mode = mode;
3234 change->args[valid].u.member = GetUserMode(channel, victim);
3235 if(!change->args[valid].u.member)
3237 if(validate && !validate(user, channel, victim))
3242 change->argc = valid;
3243 if(valid < (argc-1))
3244 reply("CSMSG_PROCESS_FAILED");
3247 modcmd_chanmode_announce(change);
3248 reply(action, channel->name);
3250 mod_chanmode_free(change);
3254 static CHANSERV_FUNC(cmd_op)
3256 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3259 static CHANSERV_FUNC(cmd_deop)
3261 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3264 static CHANSERV_FUNC(cmd_voice)
3266 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3269 static CHANSERV_FUNC(cmd_devoice)
3271 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3274 static CHANSERV_FUNC(cmd_opme)
3276 struct mod_chanmode change;
3279 mod_chanmode_init(&change);
3281 change.args[0].u.member = GetUserMode(channel, user);
3282 if(!change.args[0].u.member)
3285 reply("MSG_CHANNEL_ABSENT", channel->name);
3289 struct devnull_class *devnull;
3290 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_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_wut)
7136 char response[MAXLEN];
7137 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7138 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7139 irc_privmsg(cmd->parent->bot, channel->name, response);
7142 reply("CSMSG_WUT_RESPONSE");
7146 static CHANSERV_FUNC(cmd_8ball)
7148 unsigned int i, j, accum;
7153 for(i=1; i<argc; i++)
7154 for(j=0; argv[i][j]; j++)
7155 accum = (accum << 5) - accum + toupper(argv[i][j]);
7156 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7159 char response[MAXLEN];
7160 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7161 irc_privmsg(cmd->parent->bot, channel->name, response);
7164 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7168 static CHANSERV_FUNC(cmd_d)
7170 unsigned long sides, count, modifier, ii, total;
7171 char response[MAXLEN], *sep;
7175 if((count = strtoul(argv[1], &sep, 10)) < 1)
7185 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7186 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7190 else if((sep[0] == '-') && isdigit(sep[1]))
7191 modifier = strtoul(sep, NULL, 10);
7192 else if((sep[0] == '+') && isdigit(sep[1]))
7193 modifier = strtoul(sep+1, NULL, 10);
7200 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7205 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7208 for(total = ii = 0; ii < count; ++ii)
7209 total += (rand() % sides) + 1;
7212 if((count > 1) || modifier)
7214 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7215 sprintf(response, fmt, total, count, sides, modifier);
7219 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7220 sprintf(response, fmt, total, sides);
7223 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7225 send_message_type(4, user, cmd->parent->bot, "%s", response);
7229 static CHANSERV_FUNC(cmd_huggle)
7231 /* CTCP must be via PRIVMSG, never notice */
7233 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7235 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7240 chanserv_adjust_limit(void *data)
7242 struct mod_chanmode change;
7243 struct chanData *cData = data;
7244 struct chanNode *channel = cData->channel;
7247 if(IsSuspended(cData))
7250 cData->limitAdjusted = now;
7251 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7252 if(cData->modes.modes_set & MODE_LIMIT)
7254 if(limit > cData->modes.new_limit)
7255 limit = cData->modes.new_limit;
7256 else if(limit == cData->modes.new_limit)
7260 mod_chanmode_init(&change);
7261 change.modes_set = MODE_LIMIT;
7262 change.new_limit = limit;
7263 mod_chanmode_announce(chanserv, channel, &change);
7267 handle_new_channel(struct chanNode *channel)
7269 struct chanData *cData;
7271 if(!(cData = channel->channel_info))
7274 if(cData->modes.modes_set || cData->modes.modes_clear)
7275 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7277 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7278 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7281 void handle_new_channel_created(char *chan, struct userNode *user) {
7282 if(user->handle_info && chanserv_conf.new_channel_authed) {
7283 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7284 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7285 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7287 if(chanserv_conf.new_channel_msg)
7288 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7291 /* Welcome to my worst nightmare. Warning: Read (or modify)
7292 the code below at your own risk. */
7294 handle_join(struct modeNode *mNode)
7296 struct mod_chanmode change;
7297 struct userNode *user = mNode->user;
7298 struct chanNode *channel = mNode->channel;
7299 struct chanData *cData;
7300 struct userData *uData = NULL;
7301 struct banData *bData;
7302 struct handle_info *handle;
7303 unsigned int modes = 0, info = 0;
7307 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7310 cData = channel->channel_info;
7311 if(channel->members.used > cData->max) {
7312 cData->max = channel->members.used;
7313 cData->max_time = now;
7316 for(i = 0; i < channel->invited.used; i++)
7318 if(channel->invited.list[i] == user) {
7319 userList_remove(&channel->invited, user);
7323 /* Check for bans. If they're joining through a ban, one of two
7325 * 1: Join during a netburst, by riding the break. Kick them
7326 * unless they have ops or voice in the channel.
7327 * 2: They're allowed to join through the ban (an invite in
7328 * ircu2.10, or a +e on Hybrid, or something).
7329 * If they're not joining through a ban, and the banlist is not
7330 * full, see if they're on the banlist for the channel. If so,
7333 if(user->uplink->burst && !mNode->modes)
7336 for(ii = 0; ii < channel->banlist.used; ii++)
7338 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7340 /* Riding a netburst. Naughty. */
7341 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7347 mod_chanmode_init(&change);
7349 if(channel->banlist.used < MAXBANS)
7351 /* Not joining through a ban. */
7352 for(bData = cData->bans;
7353 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7354 bData = bData->next);
7358 char kick_reason[MAXLEN];
7359 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7361 bData->triggered = now;
7362 if(bData != cData->bans)
7364 /* Shuffle the ban to the head of the list. */
7366 bData->next->prev = bData->prev;
7368 bData->prev->next = bData->next;
7371 bData->next = cData->bans;
7374 cData->bans->prev = bData;
7375 cData->bans = bData;
7378 change.args[0].mode = MODE_BAN;
7379 change.args[0].u.hostmask = bData->mask;
7380 mod_chanmode_announce(chanserv, channel, &change);
7381 KickChannelUser(user, channel, chanserv, kick_reason);
7386 /* ChanServ will not modify the limits in join-flooded channels,
7387 or when there are enough slots left below the limit. */
7388 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7389 && !channel->join_flooded
7390 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7392 /* The user count has begun "bumping" into the channel limit,
7393 so set a timer to raise the limit a bit. Any previous
7394 timers are removed so three incoming users within the delay
7395 results in one limit change, not three. */
7397 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7398 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7401 if(channel->join_flooded)
7403 /* don't automatically give ops or voice during a join flood */
7405 else if(cData->lvlOpts[lvlGiveOps] == 0)
7406 modes |= MODE_CHANOP;
7407 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7408 modes |= MODE_VOICE;
7410 greeting = cData->greeting;
7411 if(user->handle_info)
7413 handle = user->handle_info;
7415 if(IsHelper(user) && !IsHelping(user))
7418 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7420 if(channel == chanserv_conf.support_channels.list[ii])
7422 HANDLE_SET_FLAG(user->handle_info, HELPING);
7428 uData = GetTrueChannelAccess(cData, handle);
7429 if(uData && !IsUserSuspended(uData))
7431 /* Ops and above were handled by the above case. */
7432 if(IsUserAutoOp(uData))
7434 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7435 modes |= MODE_CHANOP;
7436 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7437 modes |= MODE_VOICE;
7439 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7440 cData->visited = now;
7441 if(cData->user_greeting)
7442 greeting = cData->user_greeting;
7444 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7445 && ((now - uData->seen) >= chanserv_conf.info_delay)
7453 /* If user joining normally (not during burst), apply op or voice,
7454 * and send greeting/userinfo as appropriate.
7456 if(!user->uplink->burst)
7460 if(modes & MODE_CHANOP)
7461 modes &= ~MODE_VOICE;
7462 change.args[0].mode = modes;
7463 change.args[0].u.member = mNode;
7464 mod_chanmode_announce(chanserv, channel, &change);
7467 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7468 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7469 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7475 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7477 struct mod_chanmode change;
7478 struct userData *channel;
7479 unsigned int ii, jj;
7481 if(!user->handle_info)
7484 mod_chanmode_init(&change);
7486 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7488 struct chanNode *cn;
7489 struct modeNode *mn;
7490 if(IsUserSuspended(channel)
7491 || IsSuspended(channel->channel)
7492 || !(cn = channel->channel->channel))
7495 mn = GetUserMode(cn, user);
7498 if(!IsUserSuspended(channel)
7499 && IsUserAutoInvite(channel)
7500 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7502 && !user->uplink->burst)
7503 irc_invite(chanserv, user, cn);
7507 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7508 channel->channel->visited = now;
7510 if(IsUserAutoOp(channel))
7512 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7513 change.args[0].mode = MODE_CHANOP;
7514 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7515 change.args[0].mode = MODE_VOICE;
7517 change.args[0].mode = 0;
7518 change.args[0].u.member = mn;
7519 if(change.args[0].mode)
7520 mod_chanmode_announce(chanserv, cn, &change);
7523 channel->seen = now;
7524 channel->present = 1;
7527 for(ii = 0; ii < user->channels.used; ++ii)
7529 struct chanNode *chan = user->channels.list[ii]->channel;
7530 struct banData *ban;
7532 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7533 || !chan->channel_info
7534 || IsSuspended(chan->channel_info))
7536 for(jj = 0; jj < chan->banlist.used; ++jj)
7537 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7539 if(jj < chan->banlist.used)
7541 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7543 char kick_reason[MAXLEN];
7544 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7546 change.args[0].mode = MODE_BAN;
7547 change.args[0].u.hostmask = ban->mask;
7548 mod_chanmode_announce(chanserv, chan, &change);
7549 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7550 KickChannelUser(user, chan, chanserv, kick_reason);
7551 ban->triggered = now;
7556 if(IsSupportHelper(user))
7558 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7560 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7562 HANDLE_SET_FLAG(user->handle_info, HELPING);
7570 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7572 struct chanData *cData;
7573 struct userData *uData;
7575 cData = mn->channel->channel_info;
7576 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7579 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7581 /* Allow for a bit of padding so that the limit doesn't
7582 track the user count exactly, which could get annoying. */
7583 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7585 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7586 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7590 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7592 scan_user_presence(uData, mn->user);
7594 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7595 cData->visited = now;
7598 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7601 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7602 struct chanNode *channel;
7603 struct userNode *exclude;
7604 /* When looking at the channel that is being /part'ed, we
7605 * have to skip over the client that is leaving. For
7606 * other channels, we must not do that.
7608 channel = chanserv_conf.support_channels.list[ii];
7609 exclude = (channel == mn->channel) ? mn->user : NULL;
7610 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7613 if(ii == chanserv_conf.support_channels.used)
7614 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7619 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7621 struct userData *uData;
7623 if(!channel->channel_info || !kicker || IsService(kicker)
7624 || (kicker == victim) || IsSuspended(channel->channel_info)
7625 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7628 if(protect_user(victim, kicker, channel->channel_info))
7630 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7631 KickChannelUser(kicker, channel, chanserv, reason);
7634 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7639 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7641 struct chanData *cData;
7643 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7646 cData = channel->channel_info;
7647 if(bad_topic(channel, user, channel->topic))
7649 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7650 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7651 SetChannelTopic(channel, chanserv, old_topic, 1);
7652 else if(cData->topic)
7653 SetChannelTopic(channel, chanserv, cData->topic, 1);
7656 /* With topicsnarf, grab the topic and save it as the default topic. */
7657 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7660 cData->topic = strdup(channel->topic);
7666 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7668 struct mod_chanmode *bounce = NULL;
7669 unsigned int bnc, ii;
7672 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7675 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7676 && mode_lock_violated(&channel->channel_info->modes, change))
7678 char correct[MAXLEN];
7679 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7680 mod_chanmode_format(&channel->channel_info->modes, correct);
7681 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7683 for(ii = bnc = 0; ii < change->argc; ++ii)
7685 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7687 const struct userNode *victim = change->args[ii].u.member->user;
7688 if(!protect_user(victim, user, channel->channel_info))
7691 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7694 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7695 bounce->args[bnc].u.member = GetUserMode(channel, user);
7696 if(bounce->args[bnc].u.member)
7700 bounce->args[bnc].mode = MODE_CHANOP;
7701 bounce->args[bnc].u.member = change->args[ii].u.member;
7703 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7705 else if(change->args[ii].mode & MODE_CHANOP)
7707 const struct userNode *victim = change->args[ii].u.member->user;
7708 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7711 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7712 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7713 bounce->args[bnc].u.member = change->args[ii].u.member;
7716 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7718 const char *ban = change->args[ii].u.hostmask;
7719 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7722 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7723 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7724 bounce->args[bnc].u.hostmask = strdup(ban);
7726 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7731 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7732 mod_chanmode_announce(chanserv, channel, bounce);
7733 for(ii = 0; ii < change->argc; ++ii)
7734 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7735 free((char*)bounce->args[ii].u.hostmask);
7736 mod_chanmode_free(bounce);
7741 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7743 struct chanNode *channel;
7744 struct banData *bData;
7745 struct mod_chanmode change;
7746 unsigned int ii, jj;
7747 char kick_reason[MAXLEN];
7749 mod_chanmode_init(&change);
7751 change.args[0].mode = MODE_BAN;
7752 for(ii = 0; ii < user->channels.used; ++ii)
7754 channel = user->channels.list[ii]->channel;
7755 /* Need not check for bans if they're opped or voiced. */
7756 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7758 /* Need not check for bans unless channel registration is active. */
7759 if(!channel->channel_info || IsSuspended(channel->channel_info))
7761 /* Look for a matching ban already on the channel. */
7762 for(jj = 0; jj < channel->banlist.used; ++jj)
7763 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7765 /* Need not act if we found one. */
7766 if(jj < channel->banlist.used)
7768 /* Look for a matching ban in this channel. */
7769 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7771 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7773 change.args[0].u.hostmask = bData->mask;
7774 mod_chanmode_announce(chanserv, channel, &change);
7775 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7776 KickChannelUser(user, channel, chanserv, kick_reason);
7777 bData->triggered = now;
7778 break; /* we don't need to check any more bans in the channel */
7783 static void handle_rename(struct handle_info *handle, const char *old_handle)
7785 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7789 dict_remove2(handle_dnrs, old_handle, 1);
7790 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7791 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7796 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7798 struct userNode *h_user;
7800 if(handle->channels)
7802 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7803 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7805 while(handle->channels)
7806 del_channel_user(handle->channels, 1);
7811 handle_server_link(UNUSED_ARG(struct server *server))
7813 struct chanData *cData;
7815 for(cData = channelList; cData; cData = cData->next)
7817 if(!IsSuspended(cData))
7818 cData->may_opchan = 1;
7819 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7820 && !cData->channel->join_flooded
7821 && ((cData->channel->limit - cData->channel->members.used)
7822 < chanserv_conf.adjust_threshold))
7824 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7825 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7831 chanserv_conf_read(void)
7835 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7836 struct mod_chanmode *change;
7837 struct string_list *strlist;
7838 struct chanNode *chan;
7841 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7843 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7846 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7847 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7848 chanserv_conf.support_channels.used = 0;
7849 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7851 for(ii = 0; ii < strlist->used; ++ii)
7853 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7856 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7858 channelList_append(&chanserv_conf.support_channels, chan);
7861 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7864 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7867 chan = AddChannel(str, now, str2, NULL);
7869 channelList_append(&chanserv_conf.support_channels, chan);
7871 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7872 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7873 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7874 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7875 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7876 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7877 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7878 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7879 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7880 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7881 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7882 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7883 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7884 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7885 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7886 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7887 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7888 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7889 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7890 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7891 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7892 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7893 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7894 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7895 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7896 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7897 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7898 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7899 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7900 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7901 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7903 NickChange(chanserv, str, 0);
7904 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7905 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7906 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7907 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7908 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7909 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7910 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7911 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7912 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7913 chanserv_conf.max_owned = str ? atoi(str) : 5;
7914 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7915 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7916 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7917 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7918 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7919 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7920 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7921 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7922 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7923 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7924 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7925 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7926 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7929 safestrncpy(mode_line, str, sizeof(mode_line));
7930 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7931 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7932 && (change->argc < 2))
7934 chanserv_conf.default_modes = *change;
7935 mod_chanmode_free(change);
7937 free_string_list(chanserv_conf.set_shows);
7938 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7940 strlist = string_list_copy(strlist);
7943 static const char *list[] = {
7944 /* free form text */
7945 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7946 /* options based on user level */
7947 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7948 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7949 /* multiple choice options */
7950 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7951 /* binary options */
7952 "DynLimit", "NoDelete", "expire", "Vote",
7956 strlist = alloc_string_list(ArrayLength(list)-1);
7957 for(ii=0; list[ii]; ii++)
7958 string_list_append(strlist, strdup(list[ii]));
7960 chanserv_conf.set_shows = strlist;
7961 /* We don't look things up now, in case the list refers to options
7962 * defined by modules initialized after this point. Just mark the
7963 * function list as invalid, so it will be initialized.
7965 set_shows_list.used = 0;
7966 free_string_list(chanserv_conf.eightball);
7967 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7970 strlist = string_list_copy(strlist);
7974 strlist = alloc_string_list(4);
7975 string_list_append(strlist, strdup("Yes."));
7976 string_list_append(strlist, strdup("No."));
7977 string_list_append(strlist, strdup("Maybe so."));
7979 chanserv_conf.eightball = strlist;
7980 free_string_list(chanserv_conf.old_ban_names);
7981 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7983 strlist = string_list_copy(strlist);
7985 strlist = alloc_string_list(2);
7986 chanserv_conf.old_ban_names = strlist;
7987 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7988 off_channel = str ? atoi(str) : 0;
7990 str = database_get_data(conf_node, "oper_channel", RECDB_QSTRING);
7993 chanserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
7997 chanserv_conf.oper_channel = NULL;
8002 chanserv_note_type_read(const char *key, struct record_data *rd)
8005 struct note_type *ntype;
8008 if(!(obj = GET_RECORD_OBJECT(rd)))
8010 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8013 if(!(ntype = chanserv_create_note_type(key)))
8015 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8019 /* Figure out set access */
8020 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8022 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8023 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8025 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8027 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8028 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8030 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8032 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8036 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8037 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8038 ntype->set_access.min_opserv = 0;
8041 /* Figure out visibility */
8042 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8043 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8044 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8045 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8046 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8047 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8048 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8049 ntype->visible_type = NOTE_VIS_ALL;
8051 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8053 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8054 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8058 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8060 struct vote_option *vOpt;
8063 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8065 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8069 vOpt = calloc(1, sizeof(*vOpt));
8070 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8071 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8072 vOpt->voted = str ? atoi(str) : 0;
8073 vOpt->option_id = str ? atoi(key) : 0;
8074 vOpt->option_str = strdup(key);
8075 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8079 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8081 struct handle_info *handle;
8082 struct userData *uData;
8083 char *seen, *inf, *flags, *voted, *votefor, *expires;
8084 unsigned long last_seen;
8085 unsigned short access_level;
8087 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8089 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8093 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8094 if(access_level > UL_OWNER)
8096 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8100 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8101 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8102 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8103 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8104 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8105 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8106 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8107 handle = get_handle_info(key);
8110 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8114 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8115 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8116 uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8118 if((uData->flags & USER_SUSPENDED) && uData->expires)
8120 if(uData->expires > now)
8121 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8123 uData->flags &= ~USER_SUSPENDED;
8126 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8127 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8135 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8137 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8138 unsigned long set_time, triggered_time, expires_time;
8140 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8142 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8146 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8147 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8148 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8149 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8150 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8151 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8152 if (!reason || !owner)
8155 set_time = set ? strtoul(set, NULL, 0) : now;
8156 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8158 expires_time = strtoul(s_expires, NULL, 0);
8160 expires_time = set_time + atoi(s_duration);
8164 if(!reason || (expires_time && (expires_time < now)))
8167 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8170 static struct suspended *
8171 chanserv_read_suspended(dict_t obj)
8173 struct suspended *suspended = calloc(1, sizeof(*suspended));
8177 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8178 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8179 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8180 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8181 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8182 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8183 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8184 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8185 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8186 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8190 static struct giveownership *
8191 chanserv_read_giveownership(dict_t obj)
8193 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8197 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8198 giveownership->staff_issuer = str ? strdup(str) : NULL;
8200 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8202 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8203 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8205 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8206 giveownership->reason = str ? strdup(str) : NULL;
8207 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8208 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8210 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8211 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8212 return giveownership;
8216 chanserv_channel_read(const char *key, struct record_data *hir)
8218 struct suspended *suspended;
8219 struct giveownership *giveownership;
8220 struct mod_chanmode *modes;
8221 struct chanNode *cNode;
8222 struct chanData *cData;
8223 struct dict *channel, *obj;
8224 char *str, *argv[10];
8228 channel = hir->d.object;
8230 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8233 cNode = AddChannel(key, now, NULL, NULL);
8236 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8239 cData = register_channel(cNode, str);
8242 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8246 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8248 enum levelOption lvlOpt;
8249 enum charOption chOpt;
8251 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8252 cData->flags = atoi(str);
8254 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8256 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8258 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8259 else if(levelOptions[lvlOpt].old_flag)
8261 if(cData->flags & levelOptions[lvlOpt].old_flag)
8262 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8264 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8268 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8270 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8272 cData->chOpts[chOpt] = str[0];
8275 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8277 enum levelOption lvlOpt;
8278 enum charOption chOpt;
8281 cData->flags = base64toint(str, 5);
8282 count = strlen(str += 5);
8283 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8286 if(levelOptions[lvlOpt].old_flag)
8288 if(cData->flags & levelOptions[lvlOpt].old_flag)
8289 lvl = levelOptions[lvlOpt].flag_value;
8291 lvl = levelOptions[lvlOpt].default_value;
8293 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8295 case 'c': lvl = UL_COOWNER; break;
8296 case 'm': lvl = UL_MASTER; break;
8297 case 'n': lvl = UL_OWNER+1; break;
8298 case 'o': lvl = UL_OP; break;
8299 case 'p': lvl = UL_PEON; break;
8300 case 'w': lvl = UL_OWNER; break;
8301 default: lvl = 0; break;
8303 cData->lvlOpts[lvlOpt] = lvl;
8305 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8306 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8309 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8311 cData->expiry = atoi(str);
8312 if(cData->expiry > 0) {
8313 if(cData->expiry > now) {
8314 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8316 timeq_add(1, chanserv_expire_channel, cData);
8323 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8325 suspended = chanserv_read_suspended(obj);
8326 cData->suspended = suspended;
8327 suspended->cData = cData;
8328 /* We could use suspended->expires and suspended->revoked to
8329 * set the CHANNEL_SUSPENDED flag, but we don't. */
8331 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8333 suspended = calloc(1, sizeof(*suspended));
8334 suspended->issued = 0;
8335 suspended->revoked = 0;
8336 suspended->suspender = strdup(str);
8337 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8338 suspended->expires = str ? atoi(str) : 0;
8339 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8340 suspended->reason = strdup(str ? str : "No reason");
8341 suspended->previous = NULL;
8342 cData->suspended = suspended;
8343 suspended->cData = cData;
8347 cData->flags &= ~CHANNEL_SUSPENDED;
8348 suspended = NULL; /* to squelch a warning */
8351 if(IsSuspended(cData)) {
8352 if(suspended->expires > now)
8353 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8354 else if(suspended->expires)
8355 cData->flags &= ~CHANNEL_SUSPENDED;
8358 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8360 giveownership = chanserv_read_giveownership(obj);
8361 cData->giveownership = giveownership;
8364 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8365 struct mod_chanmode change;
8366 mod_chanmode_init(&change);
8368 change.args[0].mode = MODE_CHANOP;
8369 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8370 mod_chanmode_announce(chanserv, cNode, &change);
8373 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8374 cData->registered = str ? strtoul(str, NULL, 0) : now;
8375 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8376 cData->visited = str ? strtoul(str, NULL, 0) : now;
8377 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8378 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8379 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8380 cData->max = str ? atoi(str) : 0;
8381 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8382 cData->max_time = str ? atoi(str) : 0;
8383 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8384 cData->greeting = str ? strdup(str) : NULL;
8385 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8386 cData->user_greeting = str ? strdup(str) : NULL;
8387 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8388 cData->topic_mask = str ? strdup(str) : NULL;
8389 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8390 cData->topic = str ? strdup(str) : NULL;
8392 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8394 cData->vote = str ? strdup(str) : NULL;
8395 dict_delete(cData->vote_options);
8396 cData->vote_options = dict_new();
8397 dict_set_free_data(cData->vote_options, free_vote_options);
8398 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8399 cData->vote_start = str ? atoi(str) : 0;
8400 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8401 for(it = dict_first(obj); it; it = iter_next(it)) {
8402 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8406 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8407 for(it = dict_first(obj); it; it = iter_next(it))
8409 struct record_data *rd = iter_data(it);
8410 if(rd->type != RECDB_QSTRING) continue;
8411 int advtopic_index = atoi(iter_key(it));
8412 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8413 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8416 if(!IsSuspended(cData)
8417 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8418 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8419 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8420 cData->modes = *modes;
8422 cData->modes.modes_set |= MODE_REGISTERED;
8423 if(cData->modes.argc > 1)
8424 cData->modes.argc = 1;
8425 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8426 mod_chanmode_free(modes);
8429 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8430 for(it = dict_first(obj); it; it = iter_next(it))
8431 user_read_helper(iter_key(it), iter_data(it), cData);
8433 if(!cData->users && !IsProtected(cData))
8435 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8436 unregister_channel(cData, "has empty user list.");
8440 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8441 for(it = dict_first(obj); it; it = iter_next(it))
8442 ban_read_helper(iter_key(it), iter_data(it), cData);
8444 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8445 for(it = dict_first(obj); it; it = iter_next(it))
8447 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8448 struct record_data *rd = iter_data(it);
8449 const char *note, *setter;
8451 if(rd->type != RECDB_OBJECT)
8453 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8457 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8459 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8461 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8465 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8466 if(!setter) setter = "<unknown>";
8467 chanserv_add_channel_note(cData, ntype, setter, note);
8475 chanserv_dnr_read(const char *key, struct record_data *hir)
8477 const char *setter, *reason, *str;
8478 struct do_not_register *dnr;
8479 unsigned long expiry;
8481 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8484 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8487 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8490 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8493 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8494 expiry = str ? strtoul(str, NULL, 0) : 0;
8495 if(expiry && expiry <= now)
8497 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8500 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8502 dnr->set = atoi(str);
8508 chanserv_saxdb_read(struct dict *database)
8510 struct dict *section;
8513 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8514 for(it = dict_first(section); it; it = iter_next(it))
8515 chanserv_note_type_read(iter_key(it), iter_data(it));
8517 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8518 for(it = dict_first(section); it; it = iter_next(it))
8519 chanserv_channel_read(iter_key(it), iter_data(it));
8521 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8522 for(it = dict_first(section); it; it = iter_next(it))
8523 chanserv_dnr_read(iter_key(it), iter_data(it));
8529 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8531 int high_present = 0;
8532 saxdb_start_record(ctx, KEY_USERS, 1);
8533 for(; uData; uData = uData->next)
8535 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8537 saxdb_start_record(ctx, uData->handle->handle, 0);
8538 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8539 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8541 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8543 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8544 if(uData->channel->vote && uData->voted)
8545 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8546 if(uData->channel->vote && uData->votefor)
8547 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8549 saxdb_write_string(ctx, KEY_INFO, uData->info);
8550 saxdb_end_record(ctx);
8552 saxdb_end_record(ctx);
8553 return high_present;
8557 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8561 saxdb_start_record(ctx, KEY_BANS, 1);
8562 for(; bData; bData = bData->next)
8564 saxdb_start_record(ctx, bData->mask, 0);
8565 saxdb_write_int(ctx, KEY_SET, bData->set);
8566 if(bData->triggered)
8567 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8569 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8571 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8573 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8574 saxdb_end_record(ctx);
8576 saxdb_end_record(ctx);
8580 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8582 saxdb_start_record(ctx, name, 0);
8583 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8584 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8586 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8588 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8590 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8592 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8593 saxdb_end_record(ctx);
8597 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8599 saxdb_start_record(ctx, name, 0);
8600 if(giveownership->staff_issuer)
8601 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8602 if(giveownership->old_owner)
8603 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8604 if(giveownership->target)
8605 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8606 if(giveownership->target_access)
8607 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8608 if(giveownership->reason)
8609 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8610 if(giveownership->issued)
8611 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8612 if(giveownership->previous)
8613 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8614 saxdb_end_record(ctx);
8618 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8622 enum levelOption lvlOpt;
8623 enum charOption chOpt;
8626 saxdb_start_record(ctx, channel->channel->name, 1);
8628 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8629 saxdb_write_int(ctx, KEY_MAX, channel->max);
8630 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8632 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8633 if(channel->registrar)
8634 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8635 if(channel->greeting)
8636 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8637 if(channel->user_greeting)
8638 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8639 if(channel->topic_mask)
8640 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8641 if(channel->suspended)
8642 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8643 if(channel->giveownership)
8644 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8646 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8649 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8650 if(channel->vote_start)
8651 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8652 if (dict_size(channel->vote_options)) {
8653 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8654 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8655 struct vote_option *vOpt = iter_data(it);
8657 sprintf(str,"%i",vOpt->option_id);
8658 saxdb_start_record(ctx, str, 0);
8660 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8662 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8663 saxdb_end_record(ctx);
8665 saxdb_end_record(ctx);
8669 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8670 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8671 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8672 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8673 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8675 buf[0] = channel->chOpts[chOpt];
8677 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8679 saxdb_end_record(ctx);
8681 if(channel->modes.modes_set || channel->modes.modes_clear)
8683 mod_chanmode_format(&channel->modes, buf);
8684 saxdb_write_string(ctx, KEY_MODES, buf);
8687 high_present = chanserv_write_users(ctx, channel->users);
8688 chanserv_write_bans(ctx, channel->bans);
8690 if(channel->flags & CHANNEL_ADVTOPIC) {
8691 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8693 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8694 if(channel->advtopic[advtopic_index])
8695 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8697 saxdb_end_record(ctx);
8700 if(dict_size(channel->notes))
8704 saxdb_start_record(ctx, KEY_NOTES, 1);
8705 for(it = dict_first(channel->notes); it; it = iter_next(it))
8707 struct note *note = iter_data(it);
8708 saxdb_start_record(ctx, iter_key(it), 0);
8709 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8710 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8711 saxdb_end_record(ctx);
8713 saxdb_end_record(ctx);
8716 if(channel->ownerTransfer)
8717 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8718 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8719 saxdb_end_record(ctx);
8723 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8727 saxdb_start_record(ctx, ntype->name, 0);
8728 switch(ntype->set_access_type)
8730 case NOTE_SET_CHANNEL_ACCESS:
8731 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8733 case NOTE_SET_CHANNEL_SETTER:
8734 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8736 case NOTE_SET_PRIVILEGED: default:
8737 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8740 switch(ntype->visible_type)
8742 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8743 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8744 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8746 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8747 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8748 saxdb_end_record(ctx);
8752 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8754 struct do_not_register *dnr;
8755 dict_iterator_t it, next;
8757 for(it = dict_first(dnrs); it; it = next)
8759 next = iter_next(it);
8760 dnr = iter_data(it);
8761 if(dnr->expires && dnr->expires <= now)
8763 dict_remove(dnrs, iter_key(it));
8766 saxdb_start_record(ctx, dnr->chan_name, 0);
8768 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8770 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8771 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8772 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8773 saxdb_end_record(ctx);
8778 chanserv_saxdb_write(struct saxdb_context *ctx)
8781 struct chanData *channel;
8784 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8785 for(it = dict_first(note_types); it; it = iter_next(it))
8786 chanserv_write_note_type(ctx, iter_data(it));
8787 saxdb_end_record(ctx);
8790 saxdb_start_record(ctx, KEY_DNR, 1);
8791 write_dnrs_helper(ctx, handle_dnrs);
8792 write_dnrs_helper(ctx, plain_dnrs);
8793 write_dnrs_helper(ctx, mask_dnrs);
8794 saxdb_end_record(ctx);
8797 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8798 for(channel = channelList; channel; channel = channel->next)
8799 chanserv_write_channel(ctx, channel);
8800 saxdb_end_record(ctx);
8806 chanserv_db_cleanup(void) {
8808 unreg_part_func(handle_part);
8810 unregister_channel(channelList, "terminating.");
8811 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8812 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8813 free(chanserv_conf.support_channels.list);
8814 dict_delete(handle_dnrs);
8815 dict_delete(plain_dnrs);
8816 dict_delete(mask_dnrs);
8817 dict_delete(note_types);
8818 free_string_list(chanserv_conf.eightball);
8819 free_string_list(chanserv_conf.old_ban_names);
8820 free_string_list(chanserv_conf.set_shows);
8821 free(set_shows_list.list);
8822 free(uset_shows_list.list);
8825 struct userData *helper = helperList;
8826 helperList = helperList->next;
8831 #if defined(GCC_VARMACROS)
8832 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8833 #elif defined(C99_VARMACROS)
8834 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8836 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8837 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8840 init_chanserv(const char *nick)
8842 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8843 conf_register_reload(chanserv_conf_read);
8847 reg_server_link_func(handle_server_link);
8848 reg_new_channel_func(handle_new_channel);
8849 reg_join_func(handle_join);
8850 reg_part_func(handle_part);
8851 reg_kick_func(handle_kick);
8852 reg_topic_func(handle_topic);
8853 reg_mode_change_func(handle_mode);
8854 reg_nick_change_func(handle_nick_change);
8855 reg_auth_func(handle_auth);
8858 reg_handle_rename_func(handle_rename);
8859 reg_unreg_func(handle_unreg);
8861 handle_dnrs = dict_new();
8862 dict_set_free_data(handle_dnrs, free);
8863 plain_dnrs = dict_new();
8864 dict_set_free_data(plain_dnrs, free);
8865 mask_dnrs = dict_new();
8866 dict_set_free_data(mask_dnrs, free);
8868 reg_svccmd_unbind_func(handle_svccmd_unbind);
8869 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8870 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8871 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8872 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8873 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8874 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8875 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8876 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8877 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8878 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8879 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8880 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8881 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8883 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8884 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8886 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8887 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8888 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8889 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8890 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8892 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8893 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8894 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8895 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8896 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8898 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8899 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8900 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8901 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8903 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8904 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8905 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8906 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8907 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8908 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8909 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8910 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8912 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8913 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8914 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8915 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8916 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8917 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8918 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8919 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8920 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8921 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8922 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8923 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8924 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8925 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8926 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8928 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8929 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8930 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8931 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8932 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8934 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8935 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8937 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8938 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8939 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8940 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8941 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8942 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8943 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8944 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8945 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8946 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8947 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8949 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8950 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8952 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8953 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8954 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8955 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8957 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8958 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8959 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8960 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8961 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8963 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8964 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8965 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8966 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8967 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8968 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8970 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8971 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8972 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8973 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8974 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8975 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8976 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8977 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8979 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8981 /* Channel options */
8982 DEFINE_CHANNEL_OPTION(defaulttopic);
8983 DEFINE_CHANNEL_OPTION(topicmask);
8984 DEFINE_CHANNEL_OPTION(greeting);
8985 DEFINE_CHANNEL_OPTION(usergreeting);
8986 DEFINE_CHANNEL_OPTION(modes);
8987 DEFINE_CHANNEL_OPTION(enfops);
8988 DEFINE_CHANNEL_OPTION(giveops);
8989 DEFINE_CHANNEL_OPTION(protect);
8990 DEFINE_CHANNEL_OPTION(enfmodes);
8991 DEFINE_CHANNEL_OPTION(enftopic);
8992 DEFINE_CHANNEL_OPTION(pubcmd);
8993 DEFINE_CHANNEL_OPTION(givevoice);
8994 DEFINE_CHANNEL_OPTION(userinfo);
8995 DEFINE_CHANNEL_OPTION(dynlimit);
8996 DEFINE_CHANNEL_OPTION(topicsnarf);
8997 DEFINE_CHANNEL_OPTION(vote);
8998 DEFINE_CHANNEL_OPTION(nodelete);
8999 DEFINE_CHANNEL_OPTION(toys);
9000 DEFINE_CHANNEL_OPTION(setters);
9001 DEFINE_CHANNEL_OPTION(topicrefresh);
9002 DEFINE_CHANNEL_OPTION(ctcpusers);
9003 DEFINE_CHANNEL_OPTION(ctcpreaction);
9004 DEFINE_CHANNEL_OPTION(inviteme);
9005 DEFINE_CHANNEL_OPTION(advtopic);
9006 DEFINE_CHANNEL_OPTION(unreviewed);
9007 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
9008 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9009 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9011 DEFINE_CHANNEL_OPTION(offchannel);
9012 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9014 /* Alias set topic to set defaulttopic for compatibility. */
9015 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9018 DEFINE_USER_OPTION(noautoop);
9019 DEFINE_USER_OPTION(autoinvite);
9020 DEFINE_USER_OPTION(info);
9022 /* Alias uset autovoice to uset autoop. */
9023 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9025 note_types = dict_new();
9026 dict_set_free_data(note_types, chanserv_deref_note_type);
9029 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9030 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9031 service_register(chanserv)->trigger = '!';
9032 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9034 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9036 if(chanserv_conf.channel_expire_frequency)
9037 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9039 if(chanserv_conf.dnr_expire_frequency)
9040 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9042 if(chanserv_conf.refresh_period)
9044 unsigned long next_refresh;
9045 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9046 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9049 reg_exit_func(chanserv_db_cleanup);
9050 message_register_table(msgtab);