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_OFFCHANNEL | 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 /* eject_user and unban_user flags */
527 #define ACTION_KICK 0x0001
528 #define ACTION_BAN 0x0002
529 #define ACTION_ADD_BAN 0x0004
530 #define ACTION_ADD_TIMED_BAN 0x0008
531 #define ACTION_UNBAN 0x0010
532 #define ACTION_DEL_BAN 0x0020
534 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
535 #define MODELEN 40 + KEYLEN
539 #define CSFUNC_ARGS user, channel, argc, argv, cmd
541 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
542 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
543 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
544 reply("MSG_MISSING_PARAMS", argv[0]); \
548 DECLARE_LIST(dnrList, struct do_not_register *);
549 DEFINE_LIST(dnrList, struct do_not_register *)
551 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
553 struct userNode *chanserv;
556 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
557 static struct log_type *CS_LOG;
561 struct channelList support_channels;
562 struct mod_chanmode default_modes;
564 unsigned long db_backup_frequency;
565 unsigned long channel_expire_frequency;
566 unsigned long dnr_expire_frequency;
568 unsigned long invited_timeout;
570 unsigned long info_delay;
571 unsigned long adjust_delay;
572 unsigned long channel_expire_delay;
573 unsigned int nodelete_level;
575 unsigned int adjust_threshold;
576 int join_flood_threshold;
578 unsigned int greeting_length;
579 unsigned int refresh_period;
580 unsigned int giveownership_period;
582 unsigned int max_owned;
583 unsigned int max_chan_users;
584 unsigned int max_chan_bans;
585 unsigned int min_time_bans;
586 unsigned int max_userinfo_length;
588 unsigned int revoke_mode_a;
590 struct string_list *set_shows;
591 struct string_list *eightball;
592 struct string_list *old_ban_names;
594 const char *ctcp_short_ban_duration;
595 const char *ctcp_long_ban_duration;
597 const char *irc_operator_epithet;
598 const char *network_helper_epithet;
599 const char *support_helper_epithet;
601 const char *new_channel_authed;
602 const char *new_channel_unauthed;
603 const char *new_channel_msg;
608 struct userNode *user;
609 struct userNode *bot;
610 struct chanNode *channel;
612 unsigned short lowest;
613 unsigned short highest;
614 struct userData **users;
615 struct helpfile_table table;
620 struct userNode *user;
621 struct chanNode *chan;
624 enum note_access_type
626 NOTE_SET_CHANNEL_ACCESS,
627 NOTE_SET_CHANNEL_SETTER,
631 enum note_visible_type
634 NOTE_VIS_CHANNEL_USERS,
640 enum note_access_type set_access_type;
642 unsigned int min_opserv;
643 unsigned short min_ulevel;
645 enum note_visible_type visible_type;
646 unsigned int max_length;
653 struct note_type *type;
654 char setter[NICKSERV_HANDLE_LEN+1];
658 static unsigned int registered_channels;
659 static unsigned int banCount;
661 static const struct {
664 unsigned short level;
667 { "peon", "Peon", UL_PEON, '+' },
668 { "op", "Op", UL_OP, '@' },
669 { "master", "Master", UL_MASTER, '%' },
670 { "coowner", "Coowner", UL_COOWNER, '*' },
671 { "owner", "Owner", UL_OWNER, '!' },
672 { "helper", "BUG:", UL_HELPER, 'X' }
675 static const struct {
678 unsigned short default_value;
679 unsigned int old_idx;
680 unsigned int old_flag;
681 unsigned short flag_value;
683 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
684 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
685 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
686 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
687 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
688 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
689 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
690 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
691 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
692 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
693 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
694 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
697 struct charOptionValues {
700 } protectValues[] = {
701 { 'a', "CSMSG_PROTECT_ALL" },
702 { 'e', "CSMSG_PROTECT_EQUAL" },
703 { 'l', "CSMSG_PROTECT_LOWER" },
704 { 'n', "CSMSG_PROTECT_NONE" }
706 { 'd', "CSMSG_TOYS_DISABLED" },
707 { 'n', "CSMSG_TOYS_PRIVATE" },
708 { 'p', "CSMSG_TOYS_PUBLIC" }
709 }, topicRefreshValues[] = {
710 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
711 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
712 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
713 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
714 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
715 }, ctcpReactionValues[] = {
716 { 'k', "CSMSG_CTCPREACTION_KICK" },
717 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
718 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
719 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
722 static const struct {
726 unsigned int old_idx;
728 struct charOptionValues *values;
730 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
731 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
732 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
733 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
736 struct userData *helperList;
737 struct chanData *channelList;
738 static struct module *chanserv_module;
739 static unsigned int userCount;
741 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
742 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
743 static void unregister_channel(struct chanData *channel, const char *reason);
746 user_level_from_name(const char *name, unsigned short clamp_level)
748 unsigned int level = 0, ii;
750 level = strtoul(name, NULL, 10);
751 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
752 if(!irccasecmp(name, accessLevels[ii].name))
753 level = accessLevels[ii].level;
754 if(level > clamp_level)
760 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
763 *minl = strtoul(arg, &sep, 10);
771 *maxl = strtoul(sep+1, &sep, 10);
779 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
781 struct userData *uData, **head;
783 if(!channel || !handle)
786 if(override && HANDLE_FLAGGED(handle, HELPING)
787 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
789 for(uData = helperList;
790 uData && uData->handle != handle;
791 uData = uData->next);
795 uData = calloc(1, sizeof(struct userData));
796 uData->handle = handle;
798 uData->access = UL_HELPER;
804 uData->next = helperList;
806 helperList->prev = uData;
814 for(uData = channel->users; uData; uData = uData->next)
815 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
818 head = &(channel->users);
821 if(uData && (uData != *head))
823 /* Shuffle the user to the head of whatever list he was in. */
825 uData->next->prev = uData->prev;
827 uData->prev->next = uData->next;
833 (**head).prev = uData;
840 /* Returns non-zero if user has at least the minimum access.
841 * exempt_owner is set when handling !set, so the owner can set things
844 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
846 struct userData *uData;
847 struct chanData *cData = channel->channel_info;
848 unsigned short minimum = cData->lvlOpts[opt];
851 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
854 if(minimum <= uData->access)
856 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
861 /* Scan for other users authenticated to the same handle
862 still in the channel. If so, keep them listed as present.
864 user is optional, if not null, it skips checking that userNode
865 (for the handle_part function) */
867 scan_user_presence(struct userData *uData, struct userNode *user)
871 if(IsSuspended(uData->channel)
872 || IsUserSuspended(uData)
873 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
885 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
887 unsigned int eflags, argc;
889 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
891 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
892 if(!channel->channel_info
893 || IsSuspended(channel->channel_info)
895 || !ircncasecmp(text, "ACTION ", 7))
897 /* Figure out the minimum level needed to CTCP the channel */
898 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
900 /* We need to enforce against them; do so. */
902 argv[0] = (char*)text;
903 argv[1] = user->nick;
905 if(GetUserMode(channel, user))
906 eflags |= ACTION_KICK;
907 switch(channel->channel_info->chOpts[chCTCPReaction]) {
908 default: case 'k': /* just do the kick */ break;
910 eflags |= ACTION_BAN;
913 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
914 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
917 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
918 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
921 argv[argc++] = bad_ctcp_reason;
922 eject_user(chanserv, channel, argc, argv, NULL, eflags);
926 chanserv_create_note_type(const char *name)
928 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
929 strcpy(ntype->name, name);
931 dict_insert(note_types, ntype->name, ntype);
936 free_vote_options(void *data)
938 struct vote_option *vOpt = data;
940 free(vOpt->option_str);
945 chanserv_deref_note_type(void *data)
947 struct note_type *ntype = data;
949 if(--ntype->refs > 0)
955 chanserv_flush_note_type(struct note_type *ntype)
957 struct chanData *cData;
958 for(cData = channelList; cData; cData = cData->next)
959 dict_remove(cData->notes, ntype->name);
963 chanserv_truncate_notes(struct note_type *ntype)
965 struct chanData *cData;
967 unsigned int size = sizeof(*note) + ntype->max_length;
969 for(cData = channelList; cData; cData = cData->next) {
970 note = dict_find(cData->notes, ntype->name, NULL);
973 if(strlen(note->note) <= ntype->max_length)
975 dict_remove2(cData->notes, ntype->name, 1);
976 note = realloc(note, size);
977 note->note[ntype->max_length] = 0;
978 dict_insert(cData->notes, ntype->name, note);
982 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
985 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
988 unsigned int len = strlen(text);
990 if(len > type->max_length) len = type->max_length;
991 note = calloc(1, sizeof(*note) + len);
993 strncpy(note->setter, setter, sizeof(note->setter)-1);
994 memcpy(note->note, text, len);
996 dict_insert(channel->notes, type->name, note);
1002 chanserv_free_note(void *data)
1004 struct note *note = data;
1006 chanserv_deref_note_type(note->type);
1007 assert(note->type->refs > 0); /* must use delnote to remove the type */
1011 static MODCMD_FUNC(cmd_createnote) {
1012 struct note_type *ntype;
1013 unsigned int arg = 1, existed = 0, max_length;
1015 if((ntype = dict_find(note_types, argv[1], NULL)))
1018 ntype = chanserv_create_note_type(argv[arg]);
1019 if(!irccasecmp(argv[++arg], "privileged"))
1022 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1023 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1025 else if(!irccasecmp(argv[arg], "channel"))
1027 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1030 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1033 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1034 ntype->set_access.min_ulevel = ulvl;
1036 else if(!irccasecmp(argv[arg], "setter"))
1038 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1042 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1046 if(!irccasecmp(argv[++arg], "privileged"))
1047 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1048 else if(!irccasecmp(argv[arg], "channel_users"))
1049 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1050 else if(!irccasecmp(argv[arg], "all"))
1051 ntype->visible_type = NOTE_VIS_ALL;
1053 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1057 if((arg+1) >= argc) {
1058 reply("MSG_MISSING_PARAMS", argv[0]);
1061 max_length = strtoul(argv[++arg], NULL, 0);
1062 if(max_length < 20 || max_length > 450)
1064 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1067 if(existed && (max_length < ntype->max_length))
1069 ntype->max_length = max_length;
1070 chanserv_truncate_notes(ntype);
1072 ntype->max_length = max_length;
1075 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1077 reply("CSMSG_NOTE_CREATED", ntype->name);
1082 dict_remove(note_types, ntype->name);
1086 static MODCMD_FUNC(cmd_removenote) {
1087 struct note_type *ntype;
1090 ntype = dict_find(note_types, argv[1], NULL);
1091 force = (argc > 2) && !irccasecmp(argv[2], "force");
1094 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1101 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1104 chanserv_flush_note_type(ntype);
1106 dict_remove(note_types, argv[1]);
1107 reply("CSMSG_NOTE_DELETED", argv[1]);
1112 chanserv_expire_channel(void *data)
1114 struct chanData *channel = data;
1115 char reason[MAXLEN];
1116 sprintf(reason, "channel expired.");
1117 channel->expiry = 0;
1118 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1119 unregister_channel(channel, reason);
1122 static MODCMD_FUNC(chan_opt_expire)
1124 struct chanData *cData = channel->channel_info;
1125 unsigned long value = cData->expiry;
1129 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1131 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1134 unsigned long expiry,duration;
1136 /* The two directions can have different ACLs. */
1137 if(!strcmp(argv[1], "0"))
1139 else if((duration = ParseInterval(argv[1])))
1140 expiry = now + duration;
1143 reply("MSG_INVALID_DURATION", argv[1]);
1147 if (expiry != value)
1151 timeq_del(value, chanserv_expire_channel, cData, 0);
1154 cData->expiry = value;
1157 timeq_add(expiry, chanserv_expire_channel, cData);
1162 if(cData->expiry > now) {
1163 char expirestr[INTERVALLEN];
1164 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1166 reply("CSMSG_SET_EXPIRE_OFF");
1171 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1175 if(orig->modes_set & change->modes_clear)
1177 if(orig->modes_clear & change->modes_set)
1179 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1180 && strcmp(orig->new_key, change->new_key))
1182 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1183 && (orig->new_limit != change->new_limit))
1188 static char max_length_text[MAXLEN+1][16];
1190 static struct helpfile_expansion
1191 chanserv_expand_variable(const char *variable)
1193 struct helpfile_expansion exp;
1195 if(!irccasecmp(variable, "notes"))
1198 exp.type = HF_TABLE;
1199 exp.value.table.length = 1;
1200 exp.value.table.width = 3;
1201 exp.value.table.flags = 0;
1202 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1203 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1204 exp.value.table.contents[0][0] = "Note Type";
1205 exp.value.table.contents[0][1] = "Visibility";
1206 exp.value.table.contents[0][2] = "Max Length";
1207 for(it=dict_first(note_types); it; it=iter_next(it))
1209 struct note_type *ntype = iter_data(it);
1212 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1213 row = exp.value.table.length++;
1214 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1215 exp.value.table.contents[row][0] = ntype->name;
1216 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1217 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1219 if(!max_length_text[ntype->max_length][0])
1220 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1221 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1226 exp.type = HF_STRING;
1227 exp.value.str = NULL;
1231 static struct chanData*
1232 register_channel(struct chanNode *cNode, char *registrar)
1234 struct chanData *channel;
1235 enum levelOption lvlOpt;
1236 enum charOption chOpt;
1239 channel = calloc(1, sizeof(struct chanData));
1241 channel->notes = dict_new();
1242 dict_set_free_data(channel->notes, chanserv_free_note);
1244 channel->registrar = strdup(registrar);
1245 channel->registered = now;
1246 channel->visited = now;
1247 channel->limitAdjusted = now;
1248 channel->ownerTransfer = now;
1249 channel->flags = CHANNEL_DEFAULT_FLAGS;
1250 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1251 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1252 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1253 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1254 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1255 channel->advtopic[i] = NULL;
1257 channel->prev = NULL;
1258 channel->next = channelList;
1261 channelList->prev = channel;
1262 channelList = channel;
1263 registered_channels++;
1265 channel->channel = cNode;
1267 cNode->channel_info = channel;
1269 channel->vote = NULL;
1274 static struct userData*
1275 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1277 struct userData *ud;
1279 if(access_level > UL_OWNER)
1282 ud = calloc(1, sizeof(*ud));
1283 ud->channel = channel;
1284 ud->handle = handle;
1286 ud->access = access_level;
1287 ud->info = info ? strdup(info) : NULL;
1290 ud->next = channel->users;
1292 channel->users->prev = ud;
1293 channel->users = ud;
1295 channel->userCount++;
1299 ud->u_next = ud->handle->channels;
1301 ud->u_next->u_prev = ud;
1302 ud->handle->channels = ud;
1308 del_channel_user(struct userData *user, int do_gc)
1310 struct chanData *channel = user->channel;
1312 channel->userCount--;
1316 user->prev->next = user->next;
1318 channel->users = user->next;
1320 user->next->prev = user->prev;
1323 user->u_prev->u_next = user->u_next;
1325 user->handle->channels = user->u_next;
1327 user->u_next->u_prev = user->u_prev;
1331 if(do_gc && !channel->users && !IsProtected(channel)) {
1332 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1333 unregister_channel(channel, "lost all users.");
1337 static void expire_ban(void *data);
1340 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1343 unsigned int ii, l1, l2;
1348 bd = malloc(sizeof(struct banData));
1350 bd->channel = channel;
1352 bd->triggered = triggered;
1353 bd->expires = expires;
1355 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1357 extern const char *hidden_host_suffix;
1358 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1362 l2 = strlen(old_name);
1365 if(irccasecmp(mask + l1 - l2, old_name))
1367 new_mask = alloca(MAXLEN);
1368 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1371 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1373 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1374 bd->reason = strdup(reason);
1377 timeq_add(expires, expire_ban, bd);
1380 bd->next = channel->bans;
1382 channel->bans->prev = bd;
1384 channel->banCount++;
1391 del_channel_ban(struct banData *ban)
1393 ban->channel->banCount--;
1397 ban->prev->next = ban->next;
1399 ban->channel->bans = ban->next;
1402 ban->next->prev = ban->prev;
1405 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1414 expire_ban(void *data)
1416 struct banData *bd = data;
1417 if(!IsSuspended(bd->channel))
1419 struct banList bans;
1420 struct mod_chanmode change;
1422 bans = bd->channel->channel->banlist;
1423 mod_chanmode_init(&change);
1424 for(ii=0; ii<bans.used; ii++)
1426 if(!strcmp(bans.list[ii]->ban, bd->mask))
1429 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1430 change.args[0].u.hostmask = bd->mask;
1431 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1437 del_channel_ban(bd);
1440 static void chanserv_expire_suspension(void *data);
1443 unregister_channel(struct chanData *channel, const char *reason)
1445 struct mod_chanmode change;
1446 char msgbuf[MAXLEN];
1449 /* After channel unregistration, the following must be cleaned
1451 - Channel information.
1454 - Channel suspension data.
1455 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1461 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1463 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1464 mod_chanmode_init(&change);
1466 change.modes_clear |= MODE_REGISTERED;
1467 if(chanserv_conf.revoke_mode_a)
1468 change.modes_clear |= MODE_ACCESS;
1469 mod_chanmode_announce(chanserv, channel->channel, &change);
1472 while(channel->users)
1473 del_channel_user(channel->users, 0);
1475 while(channel->bans)
1476 del_channel_ban(channel->bans);
1478 free(channel->topic);
1479 free(channel->registrar);
1480 free(channel->greeting);
1481 free(channel->user_greeting);
1482 free(channel->topic_mask);
1484 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1485 if(channel->advtopic[i])
1486 free(channel->advtopic[i]);
1490 channel->prev->next = channel->next;
1492 channelList = channel->next;
1495 channel->next->prev = channel->prev;
1497 if(channel->suspended)
1499 struct chanNode *cNode = channel->channel;
1500 struct suspended *suspended, *next_suspended;
1502 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1504 next_suspended = suspended->previous;
1505 free(suspended->suspender);
1506 free(suspended->reason);
1507 if(suspended->expires)
1508 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1513 cNode->channel_info = NULL;
1516 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1517 channel->channel->channel_info = NULL;
1519 dict_delete(channel->notes);
1520 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1521 if(!IsSuspended(channel))
1522 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1523 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1524 UnlockChannel(channel->channel);
1526 registered_channels--;
1530 expire_channels(void *data)
1532 struct chanData *channel, *next;
1533 struct userData *user;
1534 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1536 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1537 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1539 for(channel = channelList; channel; channel = next)
1541 next = channel->next;
1543 /* See if the channel can be expired. */
1544 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1545 || IsProtected(channel))
1548 /* Make sure there are no high-ranking users still in the channel. */
1549 for(user=channel->users; user; user=user->next)
1550 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1555 /* Unregister the channel */
1556 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1557 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1558 unregister_channel(channel, "registration expired.");
1561 if(chanserv_conf.channel_expire_frequency && !data)
1562 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1566 expire_dnrs(UNUSED_ARG(void *data))
1568 dict_iterator_t it, next;
1569 struct do_not_register *dnr;
1571 for(it = dict_first(handle_dnrs); it; it = next)
1573 dnr = iter_data(it);
1574 next = iter_next(it);
1575 if(dnr->expires && dnr->expires <= now)
1576 dict_remove(handle_dnrs, dnr->chan_name + 1);
1578 for(it = dict_first(plain_dnrs); it; it = next)
1580 dnr = iter_data(it);
1581 next = iter_next(it);
1582 if(dnr->expires && dnr->expires <= now)
1583 dict_remove(plain_dnrs, dnr->chan_name + 1);
1585 for(it = dict_first(mask_dnrs); it; it = next)
1587 dnr = iter_data(it);
1588 next = iter_next(it);
1589 if(dnr->expires && dnr->expires <= now)
1590 dict_remove(mask_dnrs, dnr->chan_name + 1);
1593 if(chanserv_conf.dnr_expire_frequency)
1594 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1598 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1600 char protect = channel->chOpts[chProtect];
1601 struct userData *cs_victim, *cs_aggressor;
1603 /* Don't protect if no one is to be protected, someone is attacking
1604 himself, or if the aggressor is an IRC Operator. */
1605 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1608 /* Don't protect if the victim isn't authenticated (because they
1609 can't be a channel user), unless we are to protect non-users
1611 cs_victim = GetChannelAccess(channel, victim->handle_info);
1612 if(protect != 'a' && !cs_victim)
1615 /* Protect if the aggressor isn't a user because at this point,
1616 the aggressor can only be less than or equal to the victim. */
1617 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1621 /* If the aggressor was a user, then the victim can't be helped. */
1628 if(cs_victim->access > cs_aggressor->access)
1633 if(cs_victim->access >= cs_aggressor->access)
1642 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1644 struct chanData *cData = channel->channel_info;
1645 struct userData *cs_victim;
1647 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1648 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1649 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1651 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1659 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1661 if(IsService(victim))
1663 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1667 if(protect_user(victim, user, channel->channel_info))
1669 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1676 static struct do_not_register *
1677 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1679 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1680 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1681 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1682 strcpy(dnr->reason, reason);
1684 dnr->expires = expires;
1685 if(dnr->chan_name[0] == '*')
1686 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1687 else if(strpbrk(dnr->chan_name, "*?"))
1688 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1690 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1694 static struct dnrList
1695 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1697 struct dnrList list;
1698 dict_iterator_t it, next;
1699 struct do_not_register *dnr;
1701 dnrList_init(&list);
1703 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1705 if(dnr->expires && dnr->expires <= now)
1706 dict_remove(handle_dnrs, handle);
1707 else if(list.used < max)
1708 dnrList_append(&list, dnr);
1711 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1713 if(dnr->expires && dnr->expires <= now)
1714 dict_remove(plain_dnrs, chan_name);
1715 else if(list.used < max)
1716 dnrList_append(&list, dnr);
1721 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1723 next = iter_next(it);
1724 if(!match_ircglob(chan_name, iter_key(it)))
1726 dnr = iter_data(it);
1727 if(dnr->expires && dnr->expires <= now)
1728 dict_remove(mask_dnrs, iter_key(it));
1730 dnrList_append(&list, dnr);
1737 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1739 struct userNode *user;
1740 char buf1[INTERVALLEN];
1741 char buf2[INTERVALLEN];
1748 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1753 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1754 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1758 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1761 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1766 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1768 struct dnrList list;
1771 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1772 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1773 dnr_print_func(list.list[ii], user);
1775 reply("CSMSG_MORE_DNRS", list.used - ii);
1780 struct do_not_register *
1781 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1783 struct dnrList list;
1784 struct do_not_register *dnr;
1786 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1787 dnr = list.used ? list.list[0] : NULL;
1792 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1794 struct do_not_register *dnr;
1795 dict_iterator_t it, next;
1796 unsigned int matches = 0;
1798 for(it = dict_first(dict); it; it = next)
1800 dnr = iter_data(it);
1801 next = iter_next(it);
1802 if(dnr->expires && dnr->expires <= now)
1804 dict_remove(dict, iter_key(it));
1807 dnr_print_func(dnr, user);
1814 static CHANSERV_FUNC(cmd_noregister)
1818 unsigned long expiry, duration;
1819 unsigned int matches;
1823 reply("CSMSG_DNR_SEARCH_RESULTS");
1824 matches = send_dnrs(user, handle_dnrs);
1825 matches += send_dnrs(user, plain_dnrs);
1826 matches += send_dnrs(user, mask_dnrs);
1828 reply("MSG_MATCH_COUNT", matches);
1830 reply("MSG_NO_MATCHES");
1836 if(!IsChannelName(target) && (*target != '*'))
1838 reply("CSMSG_NOT_DNR", target);
1846 reply("MSG_INVALID_DURATION", argv[2]);
1850 if(!strcmp(argv[2], "0"))
1852 else if((duration = ParseInterval(argv[2])))
1853 expiry = now + duration;
1856 reply("MSG_INVALID_DURATION", argv[2]);
1860 reason = unsplit_string(argv + 3, argc - 3, NULL);
1861 if((*target == '*') && !get_handle_info(target + 1))
1863 reply("MSG_HANDLE_UNKNOWN", target + 1);
1866 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1867 reply("CSMSG_NOREGISTER_CHANNEL", target);
1871 reply("CSMSG_DNR_SEARCH_RESULTS");
1873 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1875 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1877 reply("MSG_NO_MATCHES");
1881 static CHANSERV_FUNC(cmd_allowregister)
1883 const char *chan_name = argv[1];
1885 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1886 || dict_remove(plain_dnrs, chan_name)
1887 || dict_remove(mask_dnrs, chan_name))
1889 reply("CSMSG_DNR_REMOVED", chan_name);
1892 reply("CSMSG_NO_SUCH_DNR", chan_name);
1897 struct userNode *source;
1901 unsigned long min_set, max_set;
1902 unsigned long min_expires, max_expires;
1907 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1909 return !((dnr->set < search->min_set)
1910 || (dnr->set > search->max_set)
1911 || (dnr->expires < search->min_expires)
1912 || (search->max_expires
1913 && ((dnr->expires == 0)
1914 || (dnr->expires > search->max_expires)))
1915 || (search->chan_mask
1916 && !match_ircglob(dnr->chan_name, search->chan_mask))
1917 || (search->setter_mask
1918 && !match_ircglob(dnr->setter, search->setter_mask))
1919 || (search->reason_mask
1920 && !match_ircglob(dnr->reason, search->reason_mask)));
1923 static struct dnr_search *
1924 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1926 struct dnr_search *discrim;
1929 discrim = calloc(1, sizeof(*discrim));
1930 discrim->source = user;
1931 discrim->chan_mask = NULL;
1932 discrim->setter_mask = NULL;
1933 discrim->reason_mask = NULL;
1934 discrim->max_set = INT_MAX;
1935 discrim->limit = 50;
1937 for(ii=0; ii<argc; ++ii)
1941 reply("MSG_MISSING_PARAMS", argv[ii]);
1944 else if(0 == irccasecmp(argv[ii], "channel"))
1946 discrim->chan_mask = argv[++ii];
1948 else if(0 == irccasecmp(argv[ii], "setter"))
1950 discrim->setter_mask = argv[++ii];
1952 else if(0 == irccasecmp(argv[ii], "reason"))
1954 discrim->reason_mask = argv[++ii];
1956 else if(0 == irccasecmp(argv[ii], "limit"))
1958 discrim->limit = strtoul(argv[++ii], NULL, 0);
1960 else if(0 == irccasecmp(argv[ii], "set"))
1962 const char *cmp = argv[++ii];
1965 discrim->min_set = now - ParseInterval(cmp + 2);
1967 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1968 } else if(cmp[0] == '=') {
1969 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1970 } else if(cmp[0] == '>') {
1972 discrim->max_set = now - ParseInterval(cmp + 2);
1974 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1976 discrim->max_set = now - (ParseInterval(cmp) - 1);
1979 else if(0 == irccasecmp(argv[ii], "expires"))
1981 const char *cmp = argv[++ii];
1984 discrim->max_expires = now + ParseInterval(cmp + 2);
1986 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1987 } else if(cmp[0] == '=') {
1988 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1989 } else if(cmp[0] == '>') {
1991 discrim->min_expires = now + ParseInterval(cmp + 2);
1993 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1995 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2000 reply("MSG_INVALID_CRITERIA", argv[ii]);
2011 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2014 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2016 struct do_not_register *dnr;
2017 dict_iterator_t next;
2022 /* Initialize local variables. */
2025 if(discrim->chan_mask)
2027 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2028 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2032 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2034 /* Check against account-based DNRs. */
2035 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2036 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2039 else if(target_fixed)
2041 /* Check against channel-based DNRs. */
2042 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2043 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2048 /* Exhaustively search account DNRs. */
2049 for(it = dict_first(handle_dnrs); it; it = next)
2051 next = iter_next(it);
2052 dnr = iter_data(it);
2053 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2057 /* Do the same for channel DNRs. */
2058 for(it = dict_first(plain_dnrs); it; it = next)
2060 next = iter_next(it);
2061 dnr = iter_data(it);
2062 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2066 /* Do the same for wildcarded channel DNRs. */
2067 for(it = dict_first(mask_dnrs); it; it = next)
2069 next = iter_next(it);
2070 dnr = iter_data(it);
2071 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2079 dnr_remove_func(struct do_not_register *match, void *extra)
2081 struct userNode *user;
2084 chan_name = alloca(strlen(match->chan_name) + 1);
2085 strcpy(chan_name, match->chan_name);
2087 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2088 || dict_remove(plain_dnrs, chan_name)
2089 || dict_remove(mask_dnrs, chan_name))
2091 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2097 dnr_count_func(struct do_not_register *match, void *extra)
2099 return 0; (void)match; (void)extra;
2102 static MODCMD_FUNC(cmd_dnrsearch)
2104 struct dnr_search *discrim;
2105 dnr_search_func action;
2106 struct svccmd *subcmd;
2107 unsigned int matches;
2110 sprintf(buf, "dnrsearch %s", argv[1]);
2111 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2114 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2117 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2119 if(!irccasecmp(argv[1], "print"))
2120 action = dnr_print_func;
2121 else if(!irccasecmp(argv[1], "remove"))
2122 action = dnr_remove_func;
2123 else if(!irccasecmp(argv[1], "count"))
2124 action = dnr_count_func;
2127 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2131 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2135 if(action == dnr_print_func)
2136 reply("CSMSG_DNR_SEARCH_RESULTS");
2137 matches = dnr_search(discrim, action, user);
2139 reply("MSG_MATCH_COUNT", matches);
2141 reply("MSG_NO_MATCHES");
2147 chanserv_get_owned_count(struct handle_info *hi)
2149 struct userData *cList;
2152 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2153 if(cList->access == UL_OWNER)
2158 static CHANSERV_FUNC(cmd_register)
2160 struct handle_info *handle;
2161 struct chanData *cData;
2162 struct modeNode *mn;
2163 char reason[MAXLEN];
2165 unsigned int new_channel, force=0;
2166 struct do_not_register *dnr;
2170 if(channel->channel_info)
2172 reply("CSMSG_ALREADY_REGGED", channel->name);
2176 if(channel->bad_channel)
2178 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2183 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2185 reply("CSMSG_MUST_BE_OPPED", channel->name);
2190 chan_name = channel->name;
2194 if((argc < 2) || !IsChannelName(argv[1]))
2196 reply("MSG_NOT_CHANNEL_NAME");
2200 if(opserv_bad_channel(argv[1]))
2202 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2207 chan_name = argv[1];
2210 if(argc >= (new_channel+2))
2212 if(!IsHelping(user))
2214 reply("CSMSG_PROXY_FORBIDDEN");
2218 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2220 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2221 dnr = chanserv_is_dnr(chan_name, handle);
2225 handle = user->handle_info;
2226 dnr = chanserv_is_dnr(chan_name, handle);
2230 if(!IsHelping(user))
2231 reply("CSMSG_DNR_CHANNEL", chan_name);
2233 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2237 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2239 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2244 channel = AddChannel(argv[1], now, NULL, NULL);
2246 cData = register_channel(channel, user->handle_info->handle);
2247 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2248 cData->modes = chanserv_conf.default_modes;
2250 cData->modes.modes_set |= MODE_REGISTERED;
2251 if (IsOffChannel(cData))
2253 mod_chanmode_announce(chanserv, channel, &cData->modes);
2257 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2258 change->args[change->argc].mode = MODE_CHANOP;
2259 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2261 mod_chanmode_announce(chanserv, channel, change);
2262 mod_chanmode_free(change);
2265 /* Initialize the channel's max user record. */
2266 cData->max = channel->members.used;
2267 cData->max_time = 0;
2269 if(handle != user->handle_info)
2270 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2272 reply("CSMSG_REG_SUCCESS", channel->name);
2274 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2275 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2280 make_confirmation_string(struct userData *uData)
2282 static char strbuf[16];
2287 for(src = uData->handle->handle; *src; )
2288 accum = accum * 31 + toupper(*src++);
2290 for(src = uData->channel->channel->name; *src; )
2291 accum = accum * 31 + toupper(*src++);
2292 sprintf(strbuf, "%08x", accum);
2296 static CHANSERV_FUNC(cmd_unregister)
2299 char reason[MAXLEN];
2300 struct chanData *cData;
2301 struct userData *uData;
2303 cData = channel->channel_info;
2306 reply("CSMSG_NOT_REGISTERED", channel->name);
2310 uData = GetChannelUser(cData, user->handle_info);
2311 if(!uData || (uData->access < UL_OWNER))
2313 reply("CSMSG_NO_ACCESS");
2317 if(IsProtected(cData) && !IsOper(user))
2319 reply("CSMSG_UNREG_NODELETE", channel->name);
2323 if(!IsHelping(user))
2325 const char *confirm_string;
2326 if(IsSuspended(cData))
2328 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2331 confirm_string = make_confirmation_string(uData);
2332 if((argc < 2) || strcmp(argv[1], confirm_string))
2334 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2339 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2340 name = strdup(channel->name);
2341 unregister_channel(cData, reason);
2342 spamserv_cs_unregister(user, channel, manually, "unregistered");
2343 reply("CSMSG_UNREG_SUCCESS", name);
2349 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2351 extern struct userNode *spamserv;
2352 struct mod_chanmode *change;
2354 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2356 change = mod_chanmode_alloc(2);
2358 change->args[0].mode = MODE_CHANOP;
2359 change->args[0].u.member = AddChannelUser(chanserv, channel);
2360 change->args[1].mode = MODE_CHANOP;
2361 change->args[1].u.member = AddChannelUser(spamserv, channel);
2365 change = mod_chanmode_alloc(1);
2367 change->args[0].mode = MODE_CHANOP;
2368 change->args[0].u.member = AddChannelUser(chanserv, channel);
2371 mod_chanmode_announce(chanserv, channel, change);
2372 mod_chanmode_free(change);
2375 static CHANSERV_FUNC(cmd_move)
2377 struct mod_chanmode change;
2378 struct chanNode *target;
2379 struct modeNode *mn;
2380 struct userData *uData;
2381 char reason[MAXLEN];
2382 struct do_not_register *dnr;
2383 int chanserv_join = 0, spamserv_join;
2387 if(IsProtected(channel->channel_info))
2389 reply("CSMSG_MOVE_NODELETE", channel->name);
2393 if(!IsChannelName(argv[1]))
2395 reply("MSG_NOT_CHANNEL_NAME");
2399 if(opserv_bad_channel(argv[1]))
2401 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2405 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2407 for(uData = channel->channel_info->users; uData; uData = uData->next)
2409 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2411 if(!IsHelping(user))
2412 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2414 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2420 mod_chanmode_init(&change);
2421 if(!(target = GetChannel(argv[1])))
2423 target = AddChannel(argv[1], now, NULL, NULL);
2424 if(!IsSuspended(channel->channel_info))
2427 else if(target->channel_info)
2429 reply("CSMSG_ALREADY_REGGED", target->name);
2432 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2433 && !IsHelping(user))
2435 reply("CSMSG_MUST_BE_OPPED", target->name);
2438 else if(!IsSuspended(channel->channel_info))
2443 /* Clear MODE_REGISTERED from old channel, add it to new. */
2445 change.modes_clear = MODE_REGISTERED;
2446 mod_chanmode_announce(chanserv, channel, &change);
2447 change.modes_clear = 0;
2448 change.modes_set = MODE_REGISTERED;
2449 mod_chanmode_announce(chanserv, target, &change);
2452 /* Move the channel_info to the target channel; it
2453 shouldn't be necessary to clear timeq callbacks
2454 for the old channel. */
2455 target->channel_info = channel->channel_info;
2456 target->channel_info->channel = target;
2457 channel->channel_info = NULL;
2459 /* Check whether users are present in the new channel. */
2460 for(uData = target->channel_info->users; uData; uData = uData->next)
2461 scan_user_presence(uData, NULL);
2463 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2466 ss_cs_join_channel(target, spamserv_join);
2468 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2469 if(!IsSuspended(target->channel_info))
2471 char reason2[MAXLEN];
2472 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2473 DelChannelUser(chanserv, channel, reason2, 0);
2475 UnlockChannel(channel);
2476 LockChannel(target);
2477 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2478 reply("CSMSG_MOVE_SUCCESS", target->name);
2483 merge_users(struct chanData *source, struct chanData *target)
2485 struct userData *suData, *tuData, *next;
2491 /* Insert the source's users into the scratch area. */
2492 for(suData = source->users; suData; suData = suData->next)
2493 dict_insert(merge, suData->handle->handle, suData);
2495 /* Iterate through the target's users, looking for
2496 users common to both channels. The lower access is
2497 removed from either the scratch area or target user
2499 for(tuData = target->users; tuData; tuData = next)
2501 struct userData *choice;
2503 next = tuData->next;
2505 /* If a source user exists with the same handle as a target
2506 channel's user, resolve the conflict by removing one. */
2507 suData = dict_find(merge, tuData->handle->handle, NULL);
2511 /* Pick the data we want to keep. */
2512 /* If the access is the same, use the later seen time. */
2513 if(suData->access == tuData->access)
2514 choice = (suData->seen > tuData->seen) ? suData : tuData;
2515 else /* Otherwise, keep the higher access level. */
2516 choice = (suData->access > tuData->access) ? suData : tuData;
2517 /* Use the later seen time. */
2518 if(suData->seen < tuData->seen)
2519 suData->seen = tuData->seen;
2521 tuData->seen = suData->seen;
2523 /* Remove the user that wasn't picked. */
2524 if(choice == tuData)
2526 dict_remove(merge, suData->handle->handle);
2527 del_channel_user(suData, 0);
2530 del_channel_user(tuData, 0);
2533 /* Move the remaining users to the target channel. */
2534 for(it = dict_first(merge); it; it = iter_next(it))
2536 suData = iter_data(it);
2538 /* Insert the user into the target channel's linked list. */
2539 suData->prev = NULL;
2540 suData->next = target->users;
2541 suData->channel = target;
2544 target->users->prev = suData;
2545 target->users = suData;
2547 /* Update the user counts for the target channel; the
2548 source counts are left alone. */
2549 target->userCount++;
2551 /* Check whether the user is in the target channel. */
2552 scan_user_presence(suData, NULL);
2555 /* Possible to assert (source->users == NULL) here. */
2556 source->users = NULL;
2561 merge_bans(struct chanData *source, struct chanData *target)
2563 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2565 /* Hold on to the original head of the target ban list
2566 to avoid comparing source bans with source bans. */
2567 tFront = target->bans;
2569 /* Perform a totally expensive O(n*m) merge, ick. */
2570 for(sbData = source->bans; sbData; sbData = sNext)
2572 /* Flag to track whether the ban's been moved
2573 to the destination yet. */
2576 /* Possible to assert (sbData->prev == NULL) here. */
2577 sNext = sbData->next;
2579 for(tbData = tFront; tbData; tbData = tNext)
2581 tNext = tbData->next;
2583 /* Perform two comparisons between each source
2584 and target ban, conflicts are resolved by
2585 keeping the broader ban and copying the later
2586 expiration and triggered time. */
2587 if(match_ircglobs(tbData->mask, sbData->mask))
2589 /* There is a broader ban in the target channel that
2590 overrides one in the source channel; remove the
2591 source ban and break. */
2592 if(sbData->expires > tbData->expires)
2593 tbData->expires = sbData->expires;
2594 if(sbData->triggered > tbData->triggered)
2595 tbData->triggered = sbData->triggered;
2596 del_channel_ban(sbData);
2599 else if(match_ircglobs(sbData->mask, tbData->mask))
2601 /* There is a broader ban in the source channel that
2602 overrides one in the target channel; remove the
2603 target ban, fall through and move the source over. */
2604 if(tbData->expires > sbData->expires)
2605 sbData->expires = tbData->expires;
2606 if(tbData->triggered > sbData->triggered)
2607 sbData->triggered = tbData->triggered;
2608 if(tbData == tFront)
2610 del_channel_ban(tbData);
2613 /* Source bans can override multiple target bans, so
2614 we allow a source to run through this loop multiple
2615 times, but we can only move it once. */
2620 /* Remove the source ban from the source ban list. */
2622 sbData->next->prev = sbData->prev;
2624 /* Modify the source ban's associated channel. */
2625 sbData->channel = target;
2627 /* Insert the ban into the target channel's linked list. */
2628 sbData->prev = NULL;
2629 sbData->next = target->bans;
2632 target->bans->prev = sbData;
2633 target->bans = sbData;
2635 /* Update the user counts for the target channel. */
2640 /* Possible to assert (source->bans == NULL) here. */
2641 source->bans = NULL;
2645 merge_data(struct chanData *source, struct chanData *target)
2647 /* Use more recent visited and owner-transfer time; use older
2648 * registered time. Bitwise or may_opchan. Use higher max.
2649 * Do not touch last_refresh, ban count or user counts.
2651 if(source->visited > target->visited)
2652 target->visited = source->visited;
2653 if(source->registered < target->registered)
2654 target->registered = source->registered;
2655 if(source->ownerTransfer > target->ownerTransfer)
2656 target->ownerTransfer = source->ownerTransfer;
2657 if(source->may_opchan)
2658 target->may_opchan = 1;
2659 if(source->max > target->max) {
2660 target->max = source->max;
2661 target->max_time = source->max_time;
2666 merge_channel(struct chanData *source, struct chanData *target)
2668 merge_users(source, target);
2669 merge_bans(source, target);
2670 merge_data(source, target);
2673 static CHANSERV_FUNC(cmd_merge)
2675 struct userData *target_user;
2676 struct chanNode *target;
2677 char reason[MAXLEN];
2681 /* Make sure the target channel exists and is registered to the user
2682 performing the command. */
2683 if(!(target = GetChannel(argv[1])))
2685 reply("MSG_INVALID_CHANNEL");
2689 if(!target->channel_info)
2691 reply("CSMSG_NOT_REGISTERED", target->name);
2695 if(IsProtected(channel->channel_info))
2697 reply("CSMSG_MERGE_NODELETE");
2701 if(IsSuspended(target->channel_info))
2703 reply("CSMSG_MERGE_SUSPENDED");
2707 if(channel == target)
2709 reply("CSMSG_MERGE_SELF");
2713 target_user = GetChannelUser(target->channel_info, user->handle_info);
2714 if(!target_user || (target_user->access < UL_OWNER))
2716 reply("CSMSG_MERGE_NOT_OWNER");
2720 /* Merge the channel structures and associated data. */
2721 merge_channel(channel->channel_info, target->channel_info);
2722 spamserv_cs_move_merge(user, channel, target, 0);
2723 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2724 unregister_channel(channel->channel_info, reason);
2725 reply("CSMSG_MERGE_SUCCESS", target->name);
2729 static CHANSERV_FUNC(cmd_opchan)
2731 struct mod_chanmode change;
2732 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2734 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2737 channel->channel_info->may_opchan = 0;
2738 mod_chanmode_init(&change);
2740 change.args[0].mode = MODE_CHANOP;
2741 change.args[0].u.member = GetUserMode(channel, chanserv);
2742 if(!change.args[0].u.member)
2744 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2747 mod_chanmode_announce(chanserv, channel, &change);
2748 reply("CSMSG_OPCHAN_DONE", channel->name);
2752 static CHANSERV_FUNC(cmd_adduser)
2754 struct userData *actee;
2755 struct userData *actor, *real_actor;
2756 struct handle_info *handle;
2757 unsigned short access_level, override = 0;
2761 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2763 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2767 access_level = user_level_from_name(argv[2], UL_OWNER);
2770 reply("CSMSG_INVALID_ACCESS", argv[2]);
2774 actor = GetChannelUser(channel->channel_info, user->handle_info);
2775 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2777 if(actor->access <= access_level)
2779 reply("CSMSG_NO_BUMP_ACCESS");
2783 /* Trying to add someone with equal/more access? */
2784 if (!real_actor || real_actor->access <= access_level)
2785 override = CMD_LOG_OVERRIDE;
2787 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2790 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2792 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2796 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2797 scan_user_presence(actee, NULL);
2798 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2799 return 1 | override;
2802 static CHANSERV_FUNC(cmd_clvl)
2804 struct handle_info *handle;
2805 struct userData *victim;
2806 struct userData *actor, *real_actor;
2807 unsigned short new_access, override = 0;
2808 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2812 actor = GetChannelUser(channel->channel_info, user->handle_info);
2813 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2815 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2818 if(handle == user->handle_info && !privileged)
2820 reply("CSMSG_NO_SELF_CLVL");
2824 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2826 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2830 if(actor->access <= victim->access && !privileged)
2832 reply("MSG_USER_OUTRANKED", handle->handle);
2836 new_access = user_level_from_name(argv[2], UL_OWNER);
2840 reply("CSMSG_INVALID_ACCESS", argv[2]);
2844 if(new_access >= actor->access && !privileged)
2846 reply("CSMSG_NO_BUMP_ACCESS");
2850 /* Trying to clvl a equal/higher user? */
2851 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2852 override = CMD_LOG_OVERRIDE;
2853 /* Trying to clvl someone to equal/higher access? */
2854 if(!real_actor || new_access >= real_actor->access)
2855 override = CMD_LOG_OVERRIDE;
2856 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2857 * If they lower their own access it's not a big problem.
2860 victim->access = new_access;
2861 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2862 return 1 | override;
2865 static CHANSERV_FUNC(cmd_deluser)
2867 struct handle_info *handle;
2868 struct userData *victim;
2869 struct userData *actor, *real_actor;
2870 unsigned short access_level, override = 0;
2875 actor = GetChannelUser(channel->channel_info, user->handle_info);
2876 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2878 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2881 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2883 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2889 access_level = user_level_from_name(argv[1], UL_OWNER);
2892 reply("CSMSG_INVALID_ACCESS", argv[1]);
2895 if(access_level != victim->access)
2897 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2903 access_level = victim->access;
2906 if((actor->access <= victim->access) && !IsHelping(user))
2908 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2912 /* If people delete themselves it is an override, but they
2913 * could've used deleteme so we don't log it as an override
2915 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2916 override = CMD_LOG_OVERRIDE;
2918 chan_name = strdup(channel->name);
2919 del_channel_user(victim, 1);
2920 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2922 return 1 | override;
2926 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2928 struct userData *actor, *real_actor, *uData, *next;
2929 unsigned int override = 0;
2931 actor = GetChannelUser(channel->channel_info, user->handle_info);
2932 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2934 if(min_access > max_access)
2936 reply("CSMSG_BAD_RANGE", min_access, max_access);
2940 if(actor->access <= max_access)
2942 reply("CSMSG_NO_ACCESS");
2946 if(!real_actor || real_actor->access <= max_access)
2947 override = CMD_LOG_OVERRIDE;
2949 for(uData = channel->channel_info->users; uData; uData = next)
2953 if((uData->access >= min_access)
2954 && (uData->access <= max_access)
2955 && match_ircglob(uData->handle->handle, mask))
2956 del_channel_user(uData, 1);
2959 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2960 return 1 | override;
2963 static CHANSERV_FUNC(cmd_mdelowner)
2965 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2968 static CHANSERV_FUNC(cmd_mdelcoowner)
2970 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2973 static CHANSERV_FUNC(cmd_mdelmaster)
2975 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2978 static CHANSERV_FUNC(cmd_mdelop)
2980 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2983 static CHANSERV_FUNC(cmd_mdelpeon)
2985 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2989 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2991 struct banData *bData, *next;
2992 char interval[INTERVALLEN];
2994 unsigned long limit;
2997 limit = now - duration;
2998 for(bData = channel->channel_info->bans; bData; bData = next)
3002 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3005 del_channel_ban(bData);
3009 intervalString(interval, duration, user->handle_info);
3010 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3015 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3017 struct userData *actor, *uData, *next;
3018 char interval[INTERVALLEN];
3020 unsigned long limit;
3022 actor = GetChannelUser(channel->channel_info, user->handle_info);
3023 if(min_access > max_access)
3025 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3029 if(!actor || actor->access <= max_access)
3031 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3036 limit = now - duration;
3037 for(uData = channel->channel_info->users; uData; uData = next)
3041 if((uData->seen > limit)
3043 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3046 if(((uData->access >= min_access) && (uData->access <= max_access))
3047 || (!max_access && (uData->access < actor->access)))
3049 del_channel_user(uData, 1);
3057 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3059 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3063 static CHANSERV_FUNC(cmd_trim)
3065 unsigned long duration;
3066 unsigned short min_level, max_level;
3071 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3072 duration = ParseInterval(argv[2]);
3075 reply("CSMSG_CANNOT_TRIM");
3079 if(!irccasecmp(argv[1], "bans"))
3081 cmd_trim_bans(user, channel, duration);
3084 else if(!irccasecmp(argv[1], "users"))
3086 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3089 else if(parse_level_range(&min_level, &max_level, argv[1]))
3091 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3094 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3096 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3101 reply("CSMSG_INVALID_TRIM", argv[1]);
3106 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3107 to the user. cmd_all takes advantage of this. */
3108 static CHANSERV_FUNC(cmd_up)
3110 struct mod_chanmode change;
3111 struct userData *uData;
3114 mod_chanmode_init(&change);
3116 change.args[0].u.member = GetUserMode(channel, user);
3117 if(!change.args[0].u.member)
3120 reply("MSG_CHANNEL_ABSENT", channel->name);
3124 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3128 reply("CSMSG_GODMODE_UP", argv[0]);
3131 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3133 change.args[0].mode = MODE_CHANOP;
3134 errmsg = "CSMSG_ALREADY_OPPED";
3136 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3138 change.args[0].mode = MODE_VOICE;
3139 errmsg = "CSMSG_ALREADY_VOICED";
3144 reply("CSMSG_NO_ACCESS");
3147 change.args[0].mode &= ~change.args[0].u.member->modes;
3148 if(!change.args[0].mode)
3151 reply(errmsg, channel->name);
3154 modcmd_chanmode_announce(&change);
3158 static CHANSERV_FUNC(cmd_down)
3160 struct mod_chanmode change;
3162 mod_chanmode_init(&change);
3164 change.args[0].u.member = GetUserMode(channel, user);
3165 if(!change.args[0].u.member)
3168 reply("MSG_CHANNEL_ABSENT", channel->name);
3172 if(!change.args[0].u.member->modes)
3175 reply("CSMSG_ALREADY_DOWN", channel->name);
3179 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3180 modcmd_chanmode_announce(&change);
3184 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)
3186 struct userData *cList;
3188 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3190 if(IsSuspended(cList->channel)
3191 || IsUserSuspended(cList)
3192 || !GetUserMode(cList->channel->channel, user))
3195 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3201 static CHANSERV_FUNC(cmd_upall)
3203 return cmd_all(CSFUNC_ARGS, cmd_up);
3206 static CHANSERV_FUNC(cmd_downall)
3208 return cmd_all(CSFUNC_ARGS, cmd_down);
3211 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3212 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3215 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)
3217 unsigned int ii, valid;
3218 struct userNode *victim;
3219 struct mod_chanmode *change;
3221 change = mod_chanmode_alloc(argc - 1);
3223 for(ii=valid=0; ++ii < argc; )
3225 if(!(victim = GetUserH(argv[ii])))
3227 change->args[valid].mode = mode;
3228 change->args[valid].u.member = GetUserMode(channel, victim);
3229 if(!change->args[valid].u.member)
3231 if(validate && !validate(user, channel, victim))
3236 change->argc = valid;
3237 if(valid < (argc-1))
3238 reply("CSMSG_PROCESS_FAILED");
3241 modcmd_chanmode_announce(change);
3242 reply(action, channel->name);
3244 mod_chanmode_free(change);
3248 static CHANSERV_FUNC(cmd_op)
3250 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3253 static CHANSERV_FUNC(cmd_deop)
3255 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3258 static CHANSERV_FUNC(cmd_voice)
3260 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3263 static CHANSERV_FUNC(cmd_devoice)
3265 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3268 static CHANSERV_FUNC(cmd_opme)
3270 struct mod_chanmode change;
3273 mod_chanmode_init(&change);
3275 change.args[0].u.member = GetUserMode(channel, user);
3276 if(!change.args[0].u.member)
3279 reply("MSG_CHANNEL_ABSENT", channel->name);
3283 struct devnull_class *devnull;
3284 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3286 change.args[0].mode = MODE_CHANOP;
3287 errmsg = "CSMSG_ALREADY_OPPED";
3292 reply("CSMSG_NO_ACCESS");
3295 change.args[0].mode &= ~change.args[0].u.member->modes;
3296 if(!change.args[0].mode)
3299 reply(errmsg, channel->name);
3302 modcmd_chanmode_announce(&change);
3307 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3313 for(ii=0; ii<channel->members.used; ii++)
3315 struct modeNode *mn = channel->members.list[ii];
3317 if(IsService(mn->user))
3320 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3323 if(protect_user(mn->user, user, channel->channel_info))
3327 victims[(*victimCount)++] = mn;
3333 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3335 struct userNode *victim;
3336 struct modeNode **victims;
3337 unsigned int offset, n, victimCount, duration = 0;
3338 char *reason = "Bye.", *ban, *name;
3339 char interval[INTERVALLEN];
3341 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3342 REQUIRE_PARAMS(offset);
3343 if(argc > offset && IsNetServ(user))
3345 if(*argv[offset] == '$') {
3346 struct userNode *hib;
3347 const char *accountnameb = argv[offset] + 1;
3348 if(!(hib = GetUserH(accountnameb)))
3350 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3359 reason = unsplit_string(argv + offset, argc - offset, NULL);
3360 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3362 /* Truncate the reason to a length of TOPICLEN, as
3363 the ircd does; however, leave room for an ellipsis
3364 and the kicker's nick. */
3365 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3369 if((victim = GetUserH(argv[1])))
3371 victims = alloca(sizeof(victims[0]));
3372 victims[0] = GetUserMode(channel, victim);
3373 /* XXX: The comparison with ACTION_KICK is just because all
3374 * other actions can work on users outside the channel, and we
3375 * want to allow those (e.g. unbans) in that case. If we add
3376 * some other ejection action for in-channel users, change
3378 victimCount = victims[0] ? 1 : 0;
3380 if(IsService(victim))
3382 reply("MSG_SERVICE_IMMUNE", victim->nick);
3386 if((action == ACTION_KICK) && !victimCount)
3388 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3392 if(protect_user(victim, user, channel->channel_info))
3394 reply("CSMSG_USER_PROTECTED", victim->nick);
3398 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3399 name = victim->nick;
3401 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3403 struct handle_info *hi;
3404 extern const char *titlehost_suffix;
3405 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3406 const char *accountname = argv[1] + 1;
3408 if(!(hi = get_handle_info(accountname)))
3410 reply("MSG_HANDLE_UNKNOWN", accountname);
3414 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3415 victims = alloca(sizeof(victims[0]) * channel->members.used);
3417 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3419 reply("CSMSG_MASK_PROTECTED", banmask);
3423 if((action == ACTION_KICK) && (victimCount == 0))
3425 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3429 name = ban = strdup(banmask);
3433 if(!is_ircmask(argv[1]))
3435 reply("MSG_NICK_UNKNOWN", argv[1]);
3439 victims = alloca(sizeof(victims[0]) * channel->members.used);
3441 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3443 reply("CSMSG_MASK_PROTECTED", argv[1]);
3447 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3449 reply("CSMSG_LAME_MASK", argv[1]);
3453 if((action == ACTION_KICK) && (victimCount == 0))
3455 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3459 name = ban = strdup(argv[1]);
3462 /* Truncate the ban in place if necessary; we must ensure
3463 that 'ban' is a valid ban mask before sanitizing it. */
3464 sanitize_ircmask(ban);
3466 if(action & ACTION_ADD_BAN)
3468 struct banData *bData, *next;
3470 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3472 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3477 if(action & ACTION_ADD_TIMED_BAN)
3479 duration = ParseInterval(argv[2]);
3481 if(duration < chanserv_conf.min_time_bans)
3483 reply("CSMSG_DURATION_TOO_LOW");
3487 else if(duration > (86400 * 365 * 2))
3489 reply("CSMSG_DURATION_TOO_HIGH");
3495 for(bData = channel->channel_info->bans; bData; bData = next)
3497 if(match_ircglobs(bData->mask, ban))
3499 int exact = !irccasecmp(bData->mask, ban);
3501 /* The ban is redundant; there is already a ban
3502 with the same effect in place. */
3506 free(bData->reason);
3507 bData->reason = strdup(reason);
3508 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3510 reply("CSMSG_REASON_CHANGE", ban);
3514 if(exact && bData->expires)
3518 /* If the ban matches an existing one exactly,
3519 extend the expiration time if the provided
3520 duration is longer. */
3521 if(duration && (now + duration > bData->expires))
3523 bData->expires = now + duration;
3534 /* Delete the expiration timeq entry and
3535 requeue if necessary. */
3536 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3539 timeq_add(bData->expires, expire_ban, bData);
3543 /* automated kickban */
3546 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3548 reply("CSMSG_BAN_ADDED", name, channel->name);
3554 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3561 if(match_ircglobs(ban, bData->mask))
3563 /* The ban we are adding makes previously existing
3564 bans redundant; silently remove them. */
3565 del_channel_ban(bData);
3569 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);
3571 name = ban = strdup(bData->mask);
3575 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3577 extern const char *hidden_host_suffix;
3578 const char *old_name = chanserv_conf.old_ban_names->list[n];
3580 unsigned int l1, l2;
3583 l2 = strlen(old_name);
3586 if(irccasecmp(ban + l1 - l2, old_name))
3588 new_mask = malloc(MAXLEN);
3589 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3591 name = ban = new_mask;
3596 if(action & ACTION_BAN)
3598 unsigned int exists;
3599 struct mod_chanmode *change;
3601 if(channel->banlist.used >= MAXBANS)
3604 reply("CSMSG_BANLIST_FULL", channel->name);
3609 exists = ChannelBanExists(channel, ban);
3610 change = mod_chanmode_alloc(victimCount + 1);
3611 for(n = 0; n < victimCount; ++n)
3613 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3614 change->args[n].u.member = victims[n];
3618 change->args[n].mode = MODE_BAN;
3619 change->args[n++].u.hostmask = ban;
3623 modcmd_chanmode_announce(change);
3625 mod_chanmode_announce(chanserv, channel, change);
3626 mod_chanmode_free(change);
3628 if(exists && (action == ACTION_BAN))
3631 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3637 if(action & ACTION_KICK)
3639 char kick_reason[MAXLEN];
3640 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3642 for(n = 0; n < victimCount; n++)
3643 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3648 /* No response, since it was automated. */
3650 else if(action & ACTION_ADD_BAN)
3653 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3655 reply("CSMSG_BAN_ADDED", name, channel->name);
3657 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3658 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3659 else if(action & ACTION_BAN)
3660 reply("CSMSG_BAN_DONE", name, channel->name);
3661 else if(action & ACTION_KICK && victimCount)
3662 reply("CSMSG_KICK_DONE", name, channel->name);
3668 static CHANSERV_FUNC(cmd_kickban)
3670 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3673 static CHANSERV_FUNC(cmd_kick)
3675 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3678 static CHANSERV_FUNC(cmd_ban)
3680 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3683 static CHANSERV_FUNC(cmd_addban)
3685 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3688 static CHANSERV_FUNC(cmd_addtimedban)
3690 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3693 struct mod_chanmode *
3694 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3696 struct mod_chanmode *change;
3697 unsigned char *match;
3698 unsigned int ii, count;
3700 match = alloca(bans->used);
3703 for(ii = count = 0; ii < bans->used; ++ii)
3705 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3706 MATCH_USENICK | MATCH_VISIBLE);
3713 for(ii = count = 0; ii < bans->used; ++ii)
3715 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3722 change = mod_chanmode_alloc(count);
3723 for(ii = count = 0; ii < bans->used; ++ii)
3727 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3728 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3730 assert(count == change->argc);
3735 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3737 struct userNode *actee;
3743 /* may want to allow a comma delimited list of users... */
3744 if(!(actee = GetUserH(argv[1])))
3746 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3748 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3749 const char *accountname = argv[1] + 1;
3751 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3752 mask = strdup(banmask);
3754 else if(!is_ircmask(argv[1]))
3756 reply("MSG_NICK_UNKNOWN", argv[1]);
3761 mask = strdup(argv[1]);
3765 /* We don't sanitize the mask here because ircu
3767 if(action & ACTION_UNBAN)
3769 struct mod_chanmode *change;
3770 change = find_matching_bans(&channel->banlist, actee, mask);
3775 modcmd_chanmode_announce(change);
3776 for(ii = 0; ii < change->argc; ++ii)
3777 free((char*)change->args[ii].u.hostmask);
3778 mod_chanmode_free(change);
3783 if(action & ACTION_DEL_BAN)
3785 struct banData *ban, *next;
3787 ban = channel->channel_info->bans;
3791 for( ; ban && !user_matches_glob(actee, ban->mask,
3792 MATCH_USENICK | MATCH_VISIBLE);
3795 for( ; ban && !match_ircglobs(mask, ban->mask);
3800 del_channel_ban(ban);
3807 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3809 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3815 static CHANSERV_FUNC(cmd_unban)
3817 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3820 static CHANSERV_FUNC(cmd_delban)
3822 /* it doesn't necessarily have to remove the channel ban - may want
3823 to make that an option. */
3824 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3827 static CHANSERV_FUNC(cmd_unbanme)
3829 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3830 long flags = ACTION_UNBAN;
3832 /* remove permanent bans if the user has the proper access. */
3833 if(uData->access >= UL_MASTER)
3834 flags |= ACTION_DEL_BAN;
3836 argv[1] = user->nick;
3837 return unban_user(user, channel, 2, argv, cmd, flags);
3840 static CHANSERV_FUNC(cmd_unbanall)
3842 struct mod_chanmode *change;
3845 if(!channel->banlist.used)
3847 reply("CSMSG_NO_BANS", channel->name);
3851 change = mod_chanmode_alloc(channel->banlist.used);
3852 for(ii=0; ii<channel->banlist.used; ii++)
3854 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3855 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3857 modcmd_chanmode_announce(change);
3858 for(ii = 0; ii < change->argc; ++ii)
3859 free((char*)change->args[ii].u.hostmask);
3860 mod_chanmode_free(change);
3861 reply("CSMSG_BANS_REMOVED", channel->name);
3865 static CHANSERV_FUNC(cmd_open)
3867 struct mod_chanmode *change;
3870 change = find_matching_bans(&channel->banlist, user, NULL);
3872 change = mod_chanmode_alloc(0);
3873 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3874 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3875 && channel->channel_info->modes.modes_set)
3876 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3877 modcmd_chanmode_announce(change);
3878 reply("CSMSG_CHANNEL_OPENED", channel->name);
3879 for(ii = 0; ii < change->argc; ++ii)
3880 free((char*)change->args[ii].u.hostmask);
3881 mod_chanmode_free(change);
3885 static CHANSERV_FUNC(cmd_myaccess)
3887 static struct string_buffer sbuf;
3888 struct handle_info *target_handle;
3889 struct userData *uData;
3894 target_handle = user->handle_info;
3895 else if(!IsStaff(user))
3897 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3900 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3903 if(!oper_outranks(user, target_handle))
3906 if(!target_handle->channels)
3908 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3912 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3913 for(uData = target_handle->channels; uData; uData = uData->u_next)
3915 struct chanData *cData = uData->channel;
3917 unsigned int base_len;
3919 if(uData->access > UL_OWNER)
3921 if(uData->access == UL_OWNER)
3924 if(IsProtected(cData)
3925 && (target_handle != user->handle_info)
3926 && !GetTrueChannelAccess(cData, user->handle_info)
3927 && !IsNetworkHelper(user))
3930 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3931 base_len = sbuf.used;
3932 if(IsUserSuspended(uData))
3933 string_buffer_append(&sbuf, 's');
3934 if(IsUserAutoOp(uData))
3936 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3937 string_buffer_append(&sbuf, 'o');
3938 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3939 string_buffer_append(&sbuf, 'v');
3941 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3942 string_buffer_append(&sbuf, 'i');
3943 if(sbuf.used==base_len)
3946 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3948 string_buffer_append_string(&sbuf, ")]");
3949 string_buffer_append(&sbuf, '\0');
3950 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3954 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3956 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3962 static CHANSERV_FUNC(cmd_access)
3964 struct userNode *target;
3965 struct handle_info *target_handle;
3966 struct userData *uData;
3968 char prefix[MAXLEN];
3973 target_handle = target->handle_info;
3975 else if((target = GetUserH(argv[1])))
3977 target_handle = target->handle_info;
3979 else if(argv[1][0] == '*')
3981 if(!(target_handle = get_handle_info(argv[1]+1)))
3983 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3989 reply("MSG_NICK_UNKNOWN", argv[1]);
3993 assert(target || target_handle);
3995 if(target == chanserv)
3997 reply("CSMSG_IS_CHANSERV");
4005 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4010 reply("MSG_USER_AUTHENTICATE", target->nick);
4013 reply("MSG_AUTHENTICATE");
4019 const char *epithet = NULL, *type = NULL;
4022 epithet = chanserv_conf.irc_operator_epithet;
4023 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4025 else if(IsNetworkHelper(target))
4027 epithet = chanserv_conf.network_helper_epithet;
4028 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4030 else if(IsSupportHelper(target))
4032 epithet = chanserv_conf.support_helper_epithet;
4033 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4037 if(target_handle->epithet)
4038 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4040 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4042 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4046 sprintf(prefix, "%s", target_handle->handle);
4049 if(!channel->channel_info)
4051 reply("CSMSG_NOT_REGISTERED", channel->name);
4055 helping = HANDLE_FLAGGED(target_handle, HELPING)
4056 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4057 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4059 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4060 /* To prevent possible information leaks, only show infolines
4061 * if the requestor is in the channel or it's their own
4063 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4065 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4067 /* Likewise, only say it's suspended if the user has active
4068 * access in that channel or it's their own entry. */
4069 if(IsUserSuspended(uData)
4070 && (GetChannelUser(channel->channel_info, user->handle_info)
4071 || (user->handle_info == uData->handle)))
4073 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4078 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4085 def_list(struct listData *list)
4089 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4091 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4092 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4093 if(list->table.length == 1)
4095 msg = user_find_message(list->user, "MSG_NONE");
4096 send_message_type(4, list->user, list->bot, " %s", msg);
4101 userData_access_comp(const void *arg_a, const void *arg_b)
4103 const struct userData *a = *(struct userData**)arg_a;
4104 const struct userData *b = *(struct userData**)arg_b;
4106 if(a->access != b->access)
4107 res = b->access - a->access;
4109 res = irccasecmp(a->handle->handle, b->handle->handle);
4114 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4116 void (*send_list)(struct listData *);
4117 struct userData *uData;
4118 struct listData lData;
4119 unsigned int matches;
4123 lData.bot = cmd->parent->bot;
4124 lData.channel = channel;
4125 lData.lowest = lowest;
4126 lData.highest = highest;
4127 lData.search = (argc > 1) ? argv[1] : NULL;
4128 send_list = def_list;
4130 if(user->handle_info)
4132 switch(user->handle_info->userlist_style)
4134 case HI_STYLE_DEF: send_list = def_list; break;
4135 case HI_STYLE_ZOOT: send_list = def_list; break;
4139 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4141 for(uData = channel->channel_info->users; uData; uData = uData->next)
4143 if((uData->access < lowest)
4144 || (uData->access > highest)
4145 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4147 lData.users[matches++] = uData;
4149 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4151 lData.table.length = matches+1;
4152 lData.table.width = 4;
4153 lData.table.flags = TABLE_NO_FREE;
4154 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4155 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4156 lData.table.contents[0] = ary;
4159 ary[2] = "Last Seen";
4161 for(matches = 1; matches < lData.table.length; ++matches)
4163 char seen[INTERVALLEN];
4165 uData = lData.users[matches-1];
4166 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4167 lData.table.contents[matches] = ary;
4168 ary[0] = strtab(uData->access);
4169 ary[1] = uData->handle->handle;
4172 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4174 else if(!uData->seen)
4177 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4178 ary[2] = strdup(ary[2]);
4179 if(IsUserSuspended(uData))
4180 ary[3] = "Suspended";
4181 else if(HANDLE_FLAGGED(uData->handle, OPER))
4182 ary[3] = "Operator";
4183 else if(HANDLE_FLAGGED(uData->handle, HELPING))
4185 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4187 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4188 ary[3] = "Vacation";
4189 else if(HANDLE_FLAGGED(uData->handle, BOT))
4195 for(matches = 1; matches < lData.table.length; ++matches)
4197 free((char*)lData.table.contents[matches][2]);
4198 free(lData.table.contents[matches]);
4200 free(lData.table.contents[0]);
4201 free(lData.table.contents);
4202 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4206 static CHANSERV_FUNC(cmd_users)
4208 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4211 static CHANSERV_FUNC(cmd_wlist)
4213 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4216 static CHANSERV_FUNC(cmd_clist)
4218 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4221 static CHANSERV_FUNC(cmd_mlist)
4223 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4226 static CHANSERV_FUNC(cmd_olist)
4228 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4231 static CHANSERV_FUNC(cmd_plist)
4233 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4236 static CHANSERV_FUNC(cmd_bans)
4238 struct userNode *search_u = NULL;
4239 struct helpfile_table tbl;
4240 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4241 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4242 const char *msg_never, *triggered, *expires;
4243 struct banData *ban, **bans;
4247 else if(strchr(search = argv[1], '!'))
4250 search_wilds = search[strcspn(search, "?*")];
4252 else if(!(search_u = GetUserH(search)))
4253 reply("MSG_NICK_UNKNOWN", search);
4255 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4257 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4261 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4266 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4269 bans[matches++] = ban;
4274 tbl.length = matches + 1;
4275 tbl.width = 4 + timed;
4277 tbl.flags = TABLE_NO_FREE;
4278 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4279 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4280 tbl.contents[0][0] = "Mask";
4281 tbl.contents[0][1] = "Set By";
4282 tbl.contents[0][2] = "Triggered";
4285 tbl.contents[0][3] = "Expires";
4286 tbl.contents[0][4] = "Reason";
4289 tbl.contents[0][3] = "Reason";
4292 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4294 free(tbl.contents[0]);
4299 msg_never = user_find_message(user, "MSG_NEVER");
4300 for(ii = 0; ii < matches; )
4306 else if(ban->expires)
4307 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4309 expires = msg_never;
4312 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4314 triggered = msg_never;
4316 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4317 tbl.contents[ii][0] = ban->mask;
4318 tbl.contents[ii][1] = ban->owner;
4319 tbl.contents[ii][2] = strdup(triggered);
4322 tbl.contents[ii][3] = strdup(expires);
4323 tbl.contents[ii][4] = ban->reason;
4326 tbl.contents[ii][3] = ban->reason;
4328 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4329 reply("MSG_MATCH_COUNT", matches);
4330 for(ii = 1; ii < tbl.length; ++ii)
4332 free((char*)tbl.contents[ii][2]);
4334 free((char*)tbl.contents[ii][3]);
4335 free(tbl.contents[ii]);
4337 free(tbl.contents[0]);
4343 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4345 struct chanData *cData = channel->channel_info;
4346 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4348 if(cData->topic_mask)
4350 if(cData->flags & CHANNEL_ADVTOPIC)
4352 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4353 char topicmask[TOPICLEN];
4354 int skipnum, topicpos = 0;
4355 char *ptr = cData->topic_mask;
4356 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4359 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4361 topicmask[topicpos++] = '*';
4363 topicmask[topicpos++] = *ptr;
4366 topicmask[topicpos++] = *ptr;
4370 topicmask[topicpos] = 0;
4371 return !match_ircglob(new_topic, topicmask);
4374 return !match_ircglob(new_topic, cData->topic_mask);
4376 else if(cData->topic)
4377 return irccasecmp(new_topic, cData->topic);
4382 static CHANSERV_FUNC(cmd_topic)
4384 struct chanData *cData;
4387 cData = channel->channel_info;
4392 SetChannelTopic(channel, chanserv, cData->topic, 1);
4393 reply("CSMSG_TOPIC_SET", cData->topic);
4397 reply("CSMSG_NO_TOPIC", channel->name);
4401 topic = unsplit_string(argv + 1, argc - 1, NULL);
4402 /* If they say "!topic *", use an empty topic. */
4403 if((topic[0] == '*') && (topic[1] == 0))
4405 if(bad_topic(channel, user, topic))
4407 char *topic_mask = cData->topic_mask;
4410 char new_topic[TOPICLEN+1], tchar;
4411 int pos=0, starpos=-1, dpos=0, len;
4413 if(cData->flags & CHANNEL_ADVTOPIC)
4415 //first check if there is a leading 'modifier id'
4416 int advtopic_index = 0;
4419 for(; topic[pos]; pos++)
4421 if(topic[pos] == ' ')
4423 //leading number found, cut off and store value in advtopic_index
4425 advtopic_index = atoi(topic) - 1; //no zerobase
4426 topic = &topic[pos+1];
4427 /* If they say "!topic 2 *", unset advtopic id 2. */
4428 if((topic[0] == '*') && (topic[1] == 0))
4431 if(!isdigit(topic[pos]))
4434 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4437 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4440 if(cData->advtopic[advtopic_index])
4441 free(cData->advtopic[advtopic_index]);
4442 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4443 char *ptr = topic_mask;
4444 while(*ptr && (dpos <= TOPICLEN))
4450 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4451 numbuf[numpos++] = *ptr;
4454 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4456 new_topic[dpos++] = *ptr; //is % again
4459 advtopic_index--; //no zero base
4460 if(!cData->advtopic[advtopic_index])
4461 break; //just leave it empty
4462 len = strlen(cData->advtopic[advtopic_index]);
4463 if((dpos + len) > TOPICLEN)
4464 len = TOPICLEN + 1 - dpos;
4465 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4469 ptr++; /* and fall through */
4472 new_topic[dpos++] = *ptr;
4478 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4485 len = strlen(topic);
4486 if((dpos + len) > TOPICLEN)
4487 len = TOPICLEN + 1 - dpos;
4488 memcpy(new_topic+dpos, topic, len);
4492 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4493 default: new_topic[dpos++] = tchar; break;
4496 if((dpos > TOPICLEN) || tchar)
4499 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4500 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4504 new_topic[dpos] = 0;
4505 SetChannelTopic(channel, chanserv, new_topic, 1);
4507 reply("CSMSG_TOPIC_LOCKED", channel->name);
4512 SetChannelTopic(channel, chanserv, topic, 1);
4514 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4516 /* Grab the topic and save it as the default topic. */
4518 cData->topic = strdup(channel->topic);
4524 static CHANSERV_FUNC(cmd_mode)
4526 struct userData *uData;
4527 struct mod_chanmode *change;
4533 change = &channel->channel_info->modes;
4534 if(change->modes_set || change->modes_clear) {
4535 modcmd_chanmode_announce(change);
4536 reply("CSMSG_DEFAULTED_MODES", channel->name);
4538 reply("CSMSG_NO_MODES", channel->name);
4542 uData = GetChannelUser(channel->channel_info, user->handle_info);
4544 base_oplevel = MAXOPLEVEL;
4545 else if (uData->access >= UL_OWNER)
4548 base_oplevel = 1 + UL_OWNER - uData->access;
4549 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4552 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4556 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4557 && mode_lock_violated(&channel->channel_info->modes, change))
4560 mod_chanmode_format(&channel->channel_info->modes, modes);
4561 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4565 modcmd_chanmode_announce(change);
4566 mod_chanmode_format(change, fmt);
4567 mod_chanmode_free(change);
4568 reply("CSMSG_MODES_SET", fmt);
4573 chanserv_del_invite_mark(void *data)
4575 struct ChanUser *chanuser = data;
4576 struct chanNode *channel = chanuser->chan;
4578 if(!channel) return;
4579 for(i = 0; i < channel->invited.used; i++)
4581 if(channel->invited.list[i] == chanuser->user) {
4582 userList_remove(&channel->invited, chanuser->user);
4588 static CHANSERV_FUNC(cmd_invite)
4590 struct userNode *invite;
4591 struct ChanUser *chanuser;
4596 if(!(invite = GetUserH(argv[1])))
4598 reply("MSG_NICK_UNKNOWN", argv[1]);
4605 if(GetUserMode(channel, invite))
4607 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4611 for(i = 0; i < channel->invited.used; i++)
4613 if(channel->invited.list[i] == invite) {
4614 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4623 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4624 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4627 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4629 irc_invite(chanserv, invite, channel);
4631 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4633 userList_append(&channel->invited, invite);
4634 chanuser = calloc(1, sizeof(*chanuser));
4635 chanuser->user=invite;
4636 chanuser->chan=channel;
4637 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4642 static CHANSERV_FUNC(cmd_inviteme)
4644 if(GetUserMode(channel, user))
4646 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4649 if(channel->channel_info
4650 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4652 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4655 irc_invite(cmd->parent->bot, user, channel);
4659 static CHANSERV_FUNC(cmd_invitemeall)
4661 struct handle_info *target = user->handle_info;
4662 struct userData *uData;
4664 if(!target->channels)
4666 reply("CSMSG_SQUAT_ACCESS", target->handle);
4670 for(uData = target->channels; uData; uData = uData->u_next)
4672 struct chanData *cData = uData->channel;
4673 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4675 irc_invite(cmd->parent->bot, user, cData->channel);
4682 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4685 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4687 /* We display things based on two dimensions:
4688 * - Issue time: present or absent
4689 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4690 * (in order of precedence, so something both expired and revoked
4691 * only counts as revoked)
4693 combo = (suspended->issued ? 4 : 0)
4694 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4696 case 0: /* no issue time, indefinite expiration */
4697 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4699 case 1: /* no issue time, expires in future */
4700 intervalString(buf1, suspended->expires-now, user->handle_info);
4701 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4703 case 2: /* no issue time, expired */
4704 intervalString(buf1, now-suspended->expires, user->handle_info);
4705 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4707 case 3: /* no issue time, revoked */
4708 intervalString(buf1, now-suspended->revoked, user->handle_info);
4709 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4711 case 4: /* issue time set, indefinite expiration */
4712 intervalString(buf1, now-suspended->issued, user->handle_info);
4713 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4715 case 5: /* issue time set, expires in future */
4716 intervalString(buf1, now-suspended->issued, user->handle_info);
4717 intervalString(buf2, suspended->expires-now, user->handle_info);
4718 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4720 case 6: /* issue time set, expired */
4721 intervalString(buf1, now-suspended->issued, user->handle_info);
4722 intervalString(buf2, now-suspended->expires, user->handle_info);
4723 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4725 case 7: /* issue time set, revoked */
4726 intervalString(buf1, now-suspended->issued, user->handle_info);
4727 intervalString(buf2, now-suspended->revoked, user->handle_info);
4728 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4731 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4737 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4740 const char *fmt = "%a %b %d %H:%M %Y";
4741 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4743 if(giveownership->staff_issuer)
4745 if(giveownership->reason)
4746 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4747 giveownership->target, giveownership->target_access,
4748 giveownership->staff_issuer, buf, giveownership->reason);
4750 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4751 giveownership->target, giveownership->target_access,
4752 giveownership->staff_issuer, buf);
4756 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4760 static CHANSERV_FUNC(cmd_info)
4762 char modes[MAXLEN], buffer[INTERVALLEN];
4763 struct userData *uData, *owner;
4764 struct chanData *cData;
4765 struct do_not_register *dnr;
4770 cData = channel->channel_info;
4771 reply("CSMSG_CHANNEL_INFO", channel->name);
4773 uData = GetChannelUser(cData, user->handle_info);
4774 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4776 mod_chanmode_format(&cData->modes, modes);
4777 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4778 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4781 for(it = dict_first(cData->notes); it; it = iter_next(it))
4785 note = iter_data(it);
4786 if(!note_type_visible_to_user(cData, note->type, user))
4789 padding = PADLEN - 1 - strlen(iter_key(it));
4790 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4793 if(cData->max_time) {
4794 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4796 reply("CSMSG_CHANNEL_MAX", cData->max);
4798 for(owner = cData->users; owner; owner = owner->next)
4799 if(owner->access == UL_OWNER)
4800 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4801 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4802 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4803 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4805 privileged = IsStaff(user);
4807 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4808 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4809 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4811 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4812 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4814 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4816 struct suspended *suspended;
4817 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4818 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4819 show_suspension_info(cmd, user, suspended);
4821 else if(IsSuspended(cData))
4823 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4824 show_suspension_info(cmd, user, cData->suspended);
4827 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4829 struct giveownership *giveownership;
4830 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4831 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4832 show_giveownership_info(cmd, user, giveownership);
4837 static CHANSERV_FUNC(cmd_netinfo)
4839 extern unsigned long boot_time;
4840 extern unsigned long burst_length;
4841 char interval[INTERVALLEN];
4843 reply("CSMSG_NETWORK_INFO");
4844 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4845 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4846 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4847 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4848 reply("CSMSG_NETWORK_BANS", banCount);
4849 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4850 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4851 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4856 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4858 struct helpfile_table table;
4860 struct userNode *user;
4865 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4866 table.contents = alloca(list->used*sizeof(*table.contents));
4867 for(nn=0; nn<list->used; nn++)
4869 user = list->list[nn];
4870 if(user->modes & skip_flags)
4876 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4879 nick = alloca(strlen(user->nick)+3);
4880 sprintf(nick, "(%s)", user->nick);
4884 table.contents[table.length][0] = nick;
4887 table_send(chanserv, to->nick, 0, NULL, table);
4890 static CHANSERV_FUNC(cmd_ircops)
4892 reply("CSMSG_STAFF_OPERS");
4893 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4897 static CHANSERV_FUNC(cmd_helpers)
4899 reply("CSMSG_STAFF_HELPERS");
4900 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4904 static CHANSERV_FUNC(cmd_staff)
4906 reply("CSMSG_NETWORK_STAFF");
4907 cmd_ircops(CSFUNC_ARGS);
4908 cmd_helpers(CSFUNC_ARGS);
4912 static CHANSERV_FUNC(cmd_peek)
4914 struct modeNode *mn;
4915 char modes[MODELEN];
4917 struct helpfile_table table;
4918 int opcount = 0, voicecount = 0, srvcount = 0;
4920 irc_make_chanmode(channel, modes);
4922 reply("CSMSG_PEEK_INFO", channel->name);
4923 reply("CSMSG_PEEK_TOPIC", channel->topic);
4924 reply("CSMSG_PEEK_MODES", modes);
4928 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4929 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4930 for(n = 0; n < channel->members.used; n++)
4932 mn = channel->members.list[n];
4933 if(IsLocal(mn->user))
4935 else if(mn->modes & MODE_CHANOP)
4937 else if(mn->modes & MODE_VOICE)
4940 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4942 table.contents[table.length] = alloca(sizeof(**table.contents));
4943 table.contents[table.length][0] = mn->user->nick;
4947 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4948 (channel->members.used - opcount - voicecount - srvcount));
4952 reply("CSMSG_PEEK_OPS");
4953 table_send(chanserv, user->nick, 0, NULL, table);
4956 reply("CSMSG_PEEK_NO_OPS");
4960 static MODCMD_FUNC(cmd_wipeinfo)
4962 struct handle_info *victim;
4963 struct userData *ud, *actor, *real_actor;
4964 unsigned int override = 0;
4967 actor = GetChannelUser(channel->channel_info, user->handle_info);
4968 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4969 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4971 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4973 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4976 if((ud->access >= actor->access) && (ud != actor))
4978 reply("MSG_USER_OUTRANKED", victim->handle);
4981 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4982 override = CMD_LOG_OVERRIDE;
4986 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4987 return 1 | override;
4990 static CHANSERV_FUNC(cmd_resync)
4992 struct mod_chanmode *changes;
4993 struct chanData *cData = channel->channel_info;
4994 unsigned int ii, used;
4996 changes = mod_chanmode_alloc(channel->members.used * 2);
4997 for(ii = used = 0; ii < channel->members.used; ++ii)
4999 struct modeNode *mn = channel->members.list[ii];
5000 struct userData *uData;
5002 if(IsService(mn->user))
5005 uData = GetChannelAccess(cData, mn->user->handle_info);
5006 if(!cData->lvlOpts[lvlGiveOps]
5007 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
5009 if(!(mn->modes & MODE_CHANOP))
5011 if(!uData || IsUserAutoOp(uData))
5013 changes->args[used].mode = MODE_CHANOP;
5014 changes->args[used++].u.member = mn;
5015 if(!(mn->modes & MODE_VOICE))
5017 changes->args[used].mode = MODE_VOICE;
5018 changes->args[used++].u.member = mn;
5023 else if(!cData->lvlOpts[lvlGiveVoice]
5024 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5026 if(mn->modes & MODE_CHANOP)
5028 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5029 changes->args[used++].u.member = mn;
5031 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5033 changes->args[used].mode = MODE_VOICE;
5034 changes->args[used++].u.member = mn;
5041 changes->args[used].mode = MODE_REMOVE | mn->modes;
5042 changes->args[used++].u.member = mn;
5046 changes->argc = used;
5047 modcmd_chanmode_announce(changes);
5048 mod_chanmode_free(changes);
5049 reply("CSMSG_RESYNCED_USERS", channel->name);
5053 static CHANSERV_FUNC(cmd_seen)
5055 struct userData *uData;
5056 struct handle_info *handle;
5057 char seen[INTERVALLEN];
5061 if(!irccasecmp(argv[1], chanserv->nick))
5063 reply("CSMSG_IS_CHANSERV");
5067 if(!(handle = get_handle_info(argv[1])))
5069 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5073 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5075 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5080 reply("CSMSG_USER_PRESENT", handle->handle);
5081 else if(uData->seen)
5082 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5084 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5086 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5087 reply("CSMSG_USER_VACATION", handle->handle);
5092 static MODCMD_FUNC(cmd_names)
5094 struct userNode *targ;
5095 struct userData *targData;
5096 unsigned int ii, pos;
5099 for(ii=pos=0; ii<channel->members.used; ++ii)
5101 targ = channel->members.list[ii]->user;
5102 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5105 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5108 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5112 if(IsUserSuspended(targData))
5114 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5117 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5118 reply("CSMSG_END_NAMES", channel->name);
5123 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5125 switch(ntype->visible_type)
5127 case NOTE_VIS_ALL: return 1;
5128 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5129 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5134 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5136 struct userData *uData;
5138 switch(ntype->set_access_type)
5140 case NOTE_SET_CHANNEL_ACCESS:
5141 if(!user->handle_info)
5143 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5145 return uData->access >= ntype->set_access.min_ulevel;
5146 case NOTE_SET_CHANNEL_SETTER:
5147 return check_user_level(channel, user, lvlSetters, 1, 0);
5148 case NOTE_SET_PRIVILEGED: default:
5149 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5153 static CHANSERV_FUNC(cmd_note)
5155 struct chanData *cData;
5157 struct note_type *ntype;
5159 cData = channel->channel_info;
5162 reply("CSMSG_NOT_REGISTERED", channel->name);
5166 /* If no arguments, show all visible notes for the channel. */
5172 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5174 note = iter_data(it);
5175 if(!note_type_visible_to_user(cData, note->type, user))
5178 reply("CSMSG_NOTELIST_HEADER", channel->name);
5179 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5182 reply("CSMSG_NOTELIST_END", channel->name);
5184 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5186 /* If one argument, show the named note. */
5189 if((note = dict_find(cData->notes, argv[1], NULL))
5190 && note_type_visible_to_user(cData, note->type, user))
5192 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5194 else if((ntype = dict_find(note_types, argv[1], NULL))
5195 && note_type_visible_to_user(NULL, ntype, user))
5197 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5202 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5206 /* Assume they're trying to set a note. */
5210 ntype = dict_find(note_types, argv[1], NULL);
5213 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5216 else if(note_type_settable_by_user(channel, ntype, user))
5218 note_text = unsplit_string(argv+2, argc-2, NULL);
5219 if((note = dict_find(cData->notes, argv[1], NULL)))
5220 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5221 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5222 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5224 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5226 /* The note is viewable to staff only, so return 0
5227 to keep the invocation from getting logged (or
5228 regular users can see it in !events). */
5234 reply("CSMSG_NO_ACCESS");
5241 static CHANSERV_FUNC(cmd_delnote)
5246 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5247 || !note_type_settable_by_user(channel, note->type, user))
5249 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5252 dict_remove(channel->channel_info->notes, note->type->name);
5253 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5257 static CHANSERV_FUNC(cmd_events)
5259 struct logSearch discrim;
5260 struct logReport report;
5261 unsigned int matches, limit;
5263 limit = (argc > 1) ? atoi(argv[1]) : 10;
5264 if(limit < 1 || limit > 200)
5267 memset(&discrim, 0, sizeof(discrim));
5268 discrim.masks.bot = chanserv;
5269 discrim.masks.channel_name = channel->name;
5271 discrim.masks.command = argv[2];
5272 discrim.limit = limit;
5273 discrim.max_time = INT_MAX;
5274 discrim.severities = 1 << LOG_COMMAND;
5275 report.reporter = chanserv;
5277 reply("CSMSG_EVENT_SEARCH_RESULTS");
5278 matches = log_entry_search(&discrim, log_report_entry, &report);
5280 reply("MSG_MATCH_COUNT", matches);
5282 reply("MSG_NO_MATCHES");
5286 static CHANSERV_FUNC(cmd_say)
5292 msg = unsplit_string(argv + 1, argc - 1, NULL);
5293 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5295 else if(*argv[1] == '*' && argv[1][1] != '\0')
5297 struct handle_info *hi;
5298 struct userNode *authed;
5301 msg = unsplit_string(argv + 2, argc - 2, NULL);
5303 if (!(hi = get_handle_info(argv[1] + 1)))
5305 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5309 for (authed = hi->users; authed; authed = authed->next_authed)
5310 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5312 else if(GetUserH(argv[1]))
5315 msg = unsplit_string(argv + 2, argc - 2, NULL);
5316 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5320 reply("MSG_NOT_TARGET_NAME");
5326 static CHANSERV_FUNC(cmd_emote)
5332 /* CTCP is so annoying. */
5333 msg = unsplit_string(argv + 1, argc - 1, NULL);
5334 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5336 else if(*argv[1] == '*' && argv[1][1] != '\0')
5338 struct handle_info *hi;
5339 struct userNode *authed;
5342 msg = unsplit_string(argv + 2, argc - 2, NULL);
5344 if (!(hi = get_handle_info(argv[1] + 1)))
5346 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5350 for (authed = hi->users; authed; authed = authed->next_authed)
5351 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5353 else if(GetUserH(argv[1]))
5355 msg = unsplit_string(argv + 2, argc - 2, NULL);
5356 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5360 reply("MSG_NOT_TARGET_NAME");
5366 struct channelList *
5367 chanserv_support_channels(void)
5369 return &chanserv_conf.support_channels;
5372 static CHANSERV_FUNC(cmd_expire)
5374 int channel_count = registered_channels;
5375 expire_channels(chanserv);
5376 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5381 chanserv_expire_suspension(void *data)
5383 struct suspended *suspended = data;
5384 struct chanNode *channel;
5387 /* Update the channel registration data structure. */
5388 if(!suspended->expires || (now < suspended->expires))
5389 suspended->revoked = now;
5390 channel = suspended->cData->channel;
5391 suspended->cData->channel = channel;
5392 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5394 /* If appropriate, re-join ChanServ to the channel. */
5395 if(!IsOffChannel(suspended->cData))
5397 spamserv_cs_suspend(channel, 0, 0, NULL);
5398 ss_cs_join_channel(channel, 1);
5401 /* Mark everyone currently in the channel as present. */
5402 for(ii = 0; ii < channel->members.used; ++ii)
5404 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5413 static CHANSERV_FUNC(cmd_csuspend)
5415 struct suspended *suspended;
5416 char reason[MAXLEN];
5417 unsigned long expiry, duration;
5418 struct userData *uData;
5422 if(IsProtected(channel->channel_info))
5424 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5428 if(argv[1][0] == '!')
5430 else if(IsSuspended(channel->channel_info))
5432 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5433 show_suspension_info(cmd, user, channel->channel_info->suspended);
5437 if(!strcmp(argv[1], "0"))
5439 else if((duration = ParseInterval(argv[1])))
5440 expiry = now + duration;
5443 reply("MSG_INVALID_DURATION", argv[1]);
5447 unsplit_string(argv + 2, argc - 2, reason);
5449 suspended = calloc(1, sizeof(*suspended));
5450 suspended->revoked = 0;
5451 suspended->issued = now;
5452 suspended->suspender = strdup(user->handle_info->handle);
5453 suspended->expires = expiry;
5454 suspended->reason = strdup(reason);
5455 suspended->cData = channel->channel_info;
5456 suspended->previous = suspended->cData->suspended;
5457 suspended->cData->suspended = suspended;
5459 if(suspended->expires)
5460 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5462 if(IsSuspended(channel->channel_info))
5464 suspended->previous->revoked = now;
5465 if(suspended->previous->expires)
5466 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5467 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5468 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5472 /* Mark all users in channel as absent. */
5473 for(uData = channel->channel_info->users; uData; uData = uData->next)
5482 /* Mark the channel as suspended, then part. */
5483 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5484 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5485 DelChannelUser(chanserv, channel, suspended->reason, 0);
5486 reply("CSMSG_SUSPENDED", channel->name);
5487 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5488 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5493 static CHANSERV_FUNC(cmd_cunsuspend)
5495 struct suspended *suspended;
5496 char message[MAXLEN];
5498 if(!IsSuspended(channel->channel_info))
5500 reply("CSMSG_NOT_SUSPENDED", channel->name);
5504 suspended = channel->channel_info->suspended;
5506 /* Expire the suspension and join ChanServ to the channel. */
5507 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5508 chanserv_expire_suspension(suspended);
5509 reply("CSMSG_UNSUSPENDED", channel->name);
5510 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5511 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5515 typedef struct chanservSearch
5520 unsigned long unvisited;
5521 unsigned long registered;
5523 unsigned long flags;
5527 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5530 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5535 search = malloc(sizeof(struct chanservSearch));
5536 memset(search, 0, sizeof(*search));
5539 for(i = 0; i < argc; i++)
5541 /* Assume all criteria require arguments. */
5544 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5548 if(!irccasecmp(argv[i], "name"))
5549 search->name = argv[++i];
5550 else if(!irccasecmp(argv[i], "registrar"))
5551 search->registrar = argv[++i];
5552 else if(!irccasecmp(argv[i], "unvisited"))
5553 search->unvisited = ParseInterval(argv[++i]);
5554 else if(!irccasecmp(argv[i], "registered"))
5555 search->registered = ParseInterval(argv[++i]);
5556 else if(!irccasecmp(argv[i], "flags"))
5559 if(!irccasecmp(argv[i], "nodelete"))
5560 search->flags |= CHANNEL_NODELETE;
5561 else if(!irccasecmp(argv[i], "suspended"))
5562 search->flags |= CHANNEL_SUSPENDED;
5563 else if(!irccasecmp(argv[i], "unreviewed"))
5564 search->flags |= CHANNEL_UNREVIEWED;
5567 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5571 else if(!irccasecmp(argv[i], "limit"))
5572 search->limit = strtoul(argv[++i], NULL, 10);
5575 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5580 if(search->name && !strcmp(search->name, "*"))
5582 if(search->registrar && !strcmp(search->registrar, "*"))
5583 search->registrar = 0;
5592 chanserv_channel_match(struct chanData *channel, search_t search)
5594 const char *name = channel->channel->name;
5595 if((search->name && !match_ircglob(name, search->name)) ||
5596 (search->registrar && !channel->registrar) ||
5597 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5598 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5599 (search->registered && (now - channel->registered) > search->registered) ||
5600 (search->flags && ((search->flags & channel->flags) != search->flags)))
5607 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5609 struct chanData *channel;
5610 unsigned int matches = 0;
5612 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5614 if(!chanserv_channel_match(channel, search))
5624 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5629 search_print(struct chanData *channel, void *data)
5631 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5634 static CHANSERV_FUNC(cmd_search)
5637 unsigned int matches;
5638 channel_search_func action;
5642 if(!irccasecmp(argv[1], "count"))
5643 action = search_count;
5644 else if(!irccasecmp(argv[1], "print"))
5645 action = search_print;
5648 reply("CSMSG_ACTION_INVALID", argv[1]);
5652 search = chanserv_search_create(user, argc - 2, argv + 2);
5656 if(action == search_count)
5657 search->limit = INT_MAX;
5659 if(action == search_print)
5660 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5662 matches = chanserv_channel_search(search, action, user);
5665 reply("MSG_MATCH_COUNT", matches);
5667 reply("MSG_NO_MATCHES");
5673 static CHANSERV_FUNC(cmd_unvisited)
5675 struct chanData *cData;
5676 unsigned long interval = chanserv_conf.channel_expire_delay;
5677 char buffer[INTERVALLEN];
5678 unsigned int limit = 25, matches = 0;
5682 interval = ParseInterval(argv[1]);
5684 limit = atoi(argv[2]);
5687 intervalString(buffer, interval, user->handle_info);
5688 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5690 for(cData = channelList; cData && matches < limit; cData = cData->next)
5692 if((now - cData->visited) < interval)
5695 intervalString(buffer, now - cData->visited, user->handle_info);
5696 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5703 static MODCMD_FUNC(chan_opt_defaulttopic)
5709 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5711 reply("CSMSG_TOPIC_LOCKED", channel->name);
5715 topic = unsplit_string(argv+1, argc-1, NULL);
5717 free(channel->channel_info->topic);
5718 if(topic[0] == '*' && topic[1] == 0)
5720 topic = channel->channel_info->topic = NULL;
5724 topic = channel->channel_info->topic = strdup(topic);
5725 if(channel->channel_info->topic_mask
5726 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5727 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5729 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5732 if(channel->channel_info->topic)
5733 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5735 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5739 static MODCMD_FUNC(chan_opt_topicmask)
5743 struct chanData *cData = channel->channel_info;
5746 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5748 reply("CSMSG_TOPIC_LOCKED", channel->name);
5752 mask = unsplit_string(argv+1, argc-1, NULL);
5754 if(cData->topic_mask)
5755 free(cData->topic_mask);
5756 if(mask[0] == '*' && mask[1] == 0)
5758 cData->topic_mask = 0;
5762 cData->topic_mask = strdup(mask);
5764 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5765 else if(!match_ircglob(cData->topic, cData->topic_mask))
5766 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5770 if(channel->channel_info->topic_mask)
5771 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5773 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5777 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5781 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5785 if(greeting[0] == '*' && greeting[1] == 0)
5789 unsigned int length = strlen(greeting);
5790 if(length > chanserv_conf.greeting_length)
5792 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5795 *data = strdup(greeting);
5804 reply(name, user_find_message(user, "MSG_NONE"));
5808 static MODCMD_FUNC(chan_opt_greeting)
5810 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5813 static MODCMD_FUNC(chan_opt_usergreeting)
5815 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5818 static MODCMD_FUNC(chan_opt_modes)
5820 struct mod_chanmode *new_modes;
5825 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5827 reply("CSMSG_NO_ACCESS");
5830 if(argv[1][0] == '*' && argv[1][1] == 0)
5832 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5834 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)))
5836 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5839 else if(new_modes->argc > 1)
5841 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5842 mod_chanmode_free(new_modes);
5847 channel->channel_info->modes = *new_modes;
5848 modcmd_chanmode_announce(new_modes);
5849 mod_chanmode_free(new_modes);
5853 mod_chanmode_format(&channel->channel_info->modes, modes);
5855 reply("CSMSG_SET_MODES", modes);
5857 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5861 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5863 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5865 struct chanData *cData = channel->channel_info;
5870 /* Set flag according to value. */
5871 if(enabled_string(argv[1]))
5873 cData->flags |= mask;
5876 else if(disabled_string(argv[1]))
5878 cData->flags &= ~mask;
5883 reply("MSG_INVALID_BINARY", argv[1]);
5889 /* Find current option value. */
5890 value = (cData->flags & mask) ? 1 : 0;
5894 reply(name, user_find_message(user, "MSG_ON"));
5896 reply(name, user_find_message(user, "MSG_OFF"));
5900 static MODCMD_FUNC(chan_opt_nodelete)
5902 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5904 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5908 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5911 static MODCMD_FUNC(chan_opt_dynlimit)
5913 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5916 static MODCMD_FUNC(chan_opt_advtopic)
5918 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5921 static MODCMD_FUNC(chan_opt_offchannel)
5923 struct chanData *cData = channel->channel_info;
5928 /* Set flag according to value. */
5929 if(enabled_string(argv[1]))
5931 if(!IsOffChannel(cData))
5932 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5933 cData->flags |= CHANNEL_OFFCHANNEL;
5936 else if(disabled_string(argv[1]))
5938 if(IsOffChannel(cData))
5940 struct mod_chanmode change;
5941 mod_chanmode_init(&change);
5943 change.args[0].mode = MODE_CHANOP;
5944 change.args[0].u.member = AddChannelUser(chanserv, channel);
5945 mod_chanmode_announce(chanserv, channel, &change);
5947 cData->flags &= ~CHANNEL_OFFCHANNEL;
5952 reply("MSG_INVALID_BINARY", argv[1]);
5958 /* Find current option value. */
5959 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5963 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5965 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5969 static MODCMD_FUNC(chan_opt_unreviewed)
5971 struct chanData *cData = channel->channel_info;
5972 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5978 /* The two directions can have different ACLs. */
5979 if(enabled_string(argv[1]))
5981 else if(disabled_string(argv[1]))
5985 reply("MSG_INVALID_BINARY", argv[1]);
5989 if (new_value != value)
5991 struct svccmd *subcmd;
5992 char subcmd_name[32];
5994 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5995 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5998 reply("MSG_COMMAND_DISABLED", subcmd_name);
6001 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6005 cData->flags |= CHANNEL_UNREVIEWED;
6008 free(cData->registrar);
6009 cData->registrar = strdup(user->handle_info->handle);
6010 cData->flags &= ~CHANNEL_UNREVIEWED;
6017 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6019 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6023 static MODCMD_FUNC(chan_opt_defaults)
6025 struct userData *uData;
6026 struct chanData *cData;
6027 const char *confirm;
6028 enum levelOption lvlOpt;
6029 enum charOption chOpt;
6031 cData = channel->channel_info;
6032 uData = GetChannelUser(cData, user->handle_info);
6033 if(!uData || (uData->access < UL_OWNER))
6035 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6038 confirm = make_confirmation_string(uData);
6039 if((argc < 2) || strcmp(argv[1], confirm))
6041 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6044 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6045 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6046 cData->modes = chanserv_conf.default_modes;
6047 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6048 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6049 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6050 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6051 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6056 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6058 struct chanData *cData = channel->channel_info;
6059 struct userData *uData;
6060 unsigned short value;
6064 if(!check_user_level(channel, user, option, 1, 1))
6066 reply("CSMSG_CANNOT_SET");
6069 value = user_level_from_name(argv[1], UL_OWNER+1);
6070 if(!value && strcmp(argv[1], "0"))
6072 reply("CSMSG_INVALID_ACCESS", argv[1]);
6075 uData = GetChannelUser(cData, user->handle_info);
6076 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6078 reply("CSMSG_BAD_SETLEVEL");
6084 if(value > cData->lvlOpts[lvlGiveOps])
6086 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6091 if(value < cData->lvlOpts[lvlGiveVoice])
6093 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6098 /* This test only applies to owners, since non-owners
6099 * trying to set an option to above their level get caught
6100 * by the CSMSG_BAD_SETLEVEL test above.
6102 if(value > uData->access)
6104 reply("CSMSG_BAD_SETTERS");
6111 cData->lvlOpts[option] = value;
6113 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6117 static MODCMD_FUNC(chan_opt_enfops)
6119 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6122 static MODCMD_FUNC(chan_opt_giveops)
6124 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6127 static MODCMD_FUNC(chan_opt_enfmodes)
6129 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6132 static MODCMD_FUNC(chan_opt_enftopic)
6134 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6137 static MODCMD_FUNC(chan_opt_pubcmd)
6139 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6142 static MODCMD_FUNC(chan_opt_setters)
6144 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6147 static MODCMD_FUNC(chan_opt_ctcpusers)
6149 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6152 static MODCMD_FUNC(chan_opt_userinfo)
6154 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6157 static MODCMD_FUNC(chan_opt_givevoice)
6159 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6162 static MODCMD_FUNC(chan_opt_topicsnarf)
6164 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6167 static MODCMD_FUNC(chan_opt_vote)
6169 return channel_level_option(lvlVote, CSFUNC_ARGS);
6172 static MODCMD_FUNC(chan_opt_inviteme)
6174 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6178 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6180 struct chanData *cData = channel->channel_info;
6181 int count = charOptions[option].count, idx;
6185 idx = atoi(argv[1]);
6187 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6189 reply("CSMSG_INVALID_NUMERIC", idx);
6190 /* Show possible values. */
6191 for(idx = 0; idx < count; idx++)
6192 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6196 cData->chOpts[option] = charOptions[option].values[idx].value;
6200 /* Find current option value. */
6203 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6207 /* Somehow, the option value is corrupt; reset it to the default. */
6208 cData->chOpts[option] = charOptions[option].default_value;
6213 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6217 static MODCMD_FUNC(chan_opt_protect)
6219 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6222 static MODCMD_FUNC(chan_opt_toys)
6224 return channel_multiple_option(chToys, CSFUNC_ARGS);
6227 static MODCMD_FUNC(chan_opt_ctcpreaction)
6229 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6232 static MODCMD_FUNC(chan_opt_topicrefresh)
6234 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6237 static struct svccmd_list set_shows_list;
6240 handle_svccmd_unbind(struct svccmd *target) {
6242 for(ii=0; ii<set_shows_list.used; ++ii)
6243 if(target == set_shows_list.list[ii])
6244 set_shows_list.used = 0;
6247 static CHANSERV_FUNC(cmd_set)
6249 struct svccmd *subcmd;
6253 /* Check if we need to (re-)initialize set_shows_list. */
6254 if(!set_shows_list.used)
6256 if(!set_shows_list.size)
6258 set_shows_list.size = chanserv_conf.set_shows->used;
6259 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6261 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6263 const char *name = chanserv_conf.set_shows->list[ii];
6264 sprintf(buf, "%s %s", argv[0], name);
6265 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6268 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6271 svccmd_list_append(&set_shows_list, subcmd);
6277 reply("CSMSG_CHANNEL_OPTIONS");
6278 for(ii = 0; ii < set_shows_list.used; ii++)
6280 subcmd = set_shows_list.list[ii];
6281 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6286 sprintf(buf, "%s %s", argv[0], argv[1]);
6287 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6290 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6293 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6295 reply("CSMSG_NO_ACCESS");
6301 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6305 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6307 struct userData *uData;
6309 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6312 reply("CSMSG_NOT_USER", channel->name);
6318 /* Just show current option value. */
6320 else if(enabled_string(argv[1]))
6322 uData->flags |= mask;
6324 else if(disabled_string(argv[1]))
6326 uData->flags &= ~mask;
6330 reply("MSG_INVALID_BINARY", argv[1]);
6334 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6338 static MODCMD_FUNC(user_opt_noautoop)
6340 struct userData *uData;
6342 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6345 reply("CSMSG_NOT_USER", channel->name);
6348 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6349 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6351 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6354 static MODCMD_FUNC(user_opt_autoinvite)
6356 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6358 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6360 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6363 static MODCMD_FUNC(user_opt_info)
6365 struct userData *uData;
6368 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6372 /* If they got past the command restrictions (which require access)
6373 * but fail this test, we have some fool with security override on.
6375 reply("CSMSG_NOT_USER", channel->name);
6382 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6383 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6385 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6388 bp = strcspn(infoline, "\001");
6391 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6396 if(infoline[0] == '*' && infoline[1] == 0)
6399 uData->info = strdup(infoline);
6402 reply("CSMSG_USET_INFO", uData->info);
6404 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6408 struct svccmd_list uset_shows_list;
6410 static CHANSERV_FUNC(cmd_uset)
6412 struct svccmd *subcmd;
6416 /* Check if we need to (re-)initialize uset_shows_list. */
6417 if(!uset_shows_list.used)
6421 "NoAutoOp", "AutoInvite", "Info"
6424 if(!uset_shows_list.size)
6426 uset_shows_list.size = ArrayLength(options);
6427 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6429 for(ii = 0; ii < ArrayLength(options); ii++)
6431 const char *name = options[ii];
6432 sprintf(buf, "%s %s", argv[0], name);
6433 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6436 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6439 svccmd_list_append(&uset_shows_list, subcmd);
6445 /* Do this so options are presented in a consistent order. */
6446 reply("CSMSG_USER_OPTIONS");
6447 for(ii = 0; ii < uset_shows_list.used; ii++)
6448 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6452 sprintf(buf, "%s %s", argv[0], argv[1]);
6453 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6456 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6460 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6463 static CHANSERV_FUNC(cmd_giveownership)
6465 struct handle_info *new_owner_hi;
6466 struct userData *new_owner;
6467 struct userData *curr_user;
6468 struct userData *invoker;
6469 struct chanData *cData = channel->channel_info;
6470 struct do_not_register *dnr;
6471 const char *confirm;
6472 struct giveownership *giveownership;
6473 unsigned int force, override;
6474 unsigned short co_access, new_owner_old_access;
6475 char reason[MAXLEN], transfer_reason[MAXLEN];
6478 curr_user = GetChannelAccess(cData, user->handle_info);
6479 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6481 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6482 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6483 && (uData->access > 500)
6484 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6485 || uData->access < 500));
6487 if(!curr_user || (curr_user->access != UL_OWNER))
6489 struct userData *owner = NULL;
6490 for(curr_user = channel->channel_info->users;
6492 curr_user = curr_user->next)
6494 if(curr_user->access != UL_OWNER)
6498 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6505 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6507 char delay[INTERVALLEN];
6508 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6509 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6512 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6514 if(new_owner_hi == user->handle_info)
6516 reply("CSMSG_NO_TRANSFER_SELF");
6519 new_owner = GetChannelAccess(cData, new_owner_hi);
6524 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6528 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6532 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6534 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6537 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6538 if(!IsHelping(user))
6539 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6541 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6544 invoker = GetChannelUser(cData, user->handle_info);
6545 if(invoker->access <= UL_OWNER)
6547 confirm = make_confirmation_string(curr_user);
6548 if((argc < 3) || strcmp(argv[2], confirm))
6550 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6554 new_owner_old_access = new_owner->access;
6555 if(new_owner->access >= UL_COOWNER)
6556 co_access = new_owner->access;
6558 co_access = UL_COOWNER;
6559 new_owner->access = UL_OWNER;
6561 curr_user->access = co_access;
6562 cData->ownerTransfer = now;
6563 giveownership = calloc(1, sizeof(*giveownership));
6564 giveownership->issued = now;
6565 giveownership->old_owner = curr_user->handle->handle;
6566 giveownership->target = new_owner_hi->handle;
6567 giveownership->target_access = new_owner_old_access;
6570 if(argc > (2 + force))
6572 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6573 giveownership->reason = strdup(transfer_reason);
6575 giveownership->staff_issuer = strdup(user->handle_info->handle);
6578 giveownership->previous = channel->channel_info->giveownership;
6579 channel->channel_info->giveownership = giveownership;
6580 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6581 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6582 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6587 chanserv_expire_user_suspension(void *data)
6589 struct userData *target = data;
6591 target->expires = 0;
6592 target->flags &= ~USER_SUSPENDED;
6595 static CHANSERV_FUNC(cmd_suspend)
6597 struct handle_info *hi;
6598 struct userData *actor, *real_actor, *target;
6599 unsigned int override = 0;
6603 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6604 actor = GetChannelUser(channel->channel_info, user->handle_info);
6605 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6606 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6608 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6611 if(target->access >= actor->access)
6613 reply("MSG_USER_OUTRANKED", hi->handle);
6616 if(target->flags & USER_SUSPENDED)
6618 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6623 target->present = 0;
6626 if(!real_actor || target->access >= real_actor->access)
6627 override = CMD_LOG_OVERRIDE;
6628 if(!strcmp(argv[2], "0"))
6632 unsigned int duration;
6633 if(!(duration = ParseInterval(argv[2])))
6635 reply("MSG_INVALID_DURATION", argv[2]);
6638 expiry = now + duration;
6641 target->expires = expiry;
6644 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6646 target->flags |= USER_SUSPENDED;
6647 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6648 return 1 | override;
6651 static CHANSERV_FUNC(cmd_unsuspend)
6653 struct handle_info *hi;
6654 struct userData *actor, *real_actor, *target;
6655 unsigned int override = 0;
6658 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6659 actor = GetChannelUser(channel->channel_info, user->handle_info);
6660 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6661 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6663 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6666 if(target->access >= actor->access)
6668 reply("MSG_USER_OUTRANKED", hi->handle);
6671 if(!(target->flags & USER_SUSPENDED))
6673 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6676 if(!real_actor || target->access >= real_actor->access)
6677 override = CMD_LOG_OVERRIDE;
6678 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
6679 target->flags &= ~USER_SUSPENDED;
6680 scan_user_presence(target, NULL);
6681 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6682 return 1 | override;
6685 static MODCMD_FUNC(cmd_deleteme)
6687 struct handle_info *hi;
6688 struct userData *target;
6689 const char *confirm_string;
6690 unsigned short access_level;
6693 hi = user->handle_info;
6694 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6696 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6699 if(target->access == UL_OWNER)
6701 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6704 confirm_string = make_confirmation_string(target);
6705 if((argc < 2) || strcmp(argv[1], confirm_string))
6707 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6710 access_level = target->access;
6711 channel_name = strdup(channel->name);
6712 del_channel_user(target, 1);
6713 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6718 static CHANSERV_FUNC(cmd_addvote)
6720 struct chanData *cData = channel->channel_info;
6721 struct userData *uData, *target;
6722 struct handle_info *hi;
6723 if (!cData) return 0;
6725 hi = user->handle_info;
6726 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6728 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6731 if(target->access < 300) {
6732 reply("CSMSG_NO_ACCESS");
6736 reply("CSMSG_ADDVOTE_FULL");
6740 msg = unsplit_string(argv + 1, argc - 1, NULL);
6741 cData->vote = strdup(msg);
6742 cData->vote_start=0;
6743 dict_delete(cData->vote_options);
6744 cData->vote_options = dict_new();
6745 dict_set_free_data(cData->vote_options, free_vote_options);
6746 for(uData = channel->channel_info->users; uData; uData = uData->next)
6751 reply("CSMSG_ADDVOTE_DONE");
6755 static CHANSERV_FUNC(cmd_delvote)
6757 struct chanData *cData = channel->channel_info;
6758 struct userData *target;
6759 struct handle_info *hi;
6760 if (!cData) return 0;
6761 hi = user->handle_info;
6762 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6764 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6767 if(target->access < 300) {
6768 reply("CSMSG_NO_ACCESS");
6772 reply("CSMSG_NO_VOTE");
6777 reply("CSMSG_DELVOTE_DONE");
6781 static CHANSERV_FUNC(cmd_addoption)
6783 struct chanData *cData = channel->channel_info;
6784 struct userData *target;
6785 struct handle_info *hi;
6786 if (!cData) return 0;
6788 hi = user->handle_info;
6789 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6791 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6794 if(target->access < 300) {
6795 reply("CSMSG_NO_ACCESS");
6799 reply("CSMSG_NO_VOTE");
6805 msg = unsplit_string(argv + 1, argc - 1, NULL);
6808 unsigned int lastid = 1;
6809 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6810 struct vote_option *cvOpt = iter_data(it);
6811 if(cvOpt->option_id > lastid)
6812 lastid = cvOpt->option_id;
6814 struct vote_option *vOpt;
6815 vOpt = calloc(1, sizeof(*vOpt));
6816 vOpt->name = strdup(msg);
6817 vOpt->option_id = (lastid + 1);
6819 sprintf(str,"%i",(lastid + 1));
6820 vOpt->option_str = strdup(str);
6822 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6824 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6828 static CHANSERV_FUNC(cmd_deloption)
6830 struct chanData *cData = channel->channel_info;
6831 struct userData *uData, *target;
6832 struct handle_info *hi;
6833 if (!cData) return 0;
6835 hi = user->handle_info;
6836 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6838 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6841 if(target->access < 300) {
6842 reply("CSMSG_NO_ACCESS");
6846 reply("CSMSG_NO_VOTE");
6849 if(cData->vote_start) {
6850 if(dict_size(cData->vote_options) < 3) {
6851 reply("CSMSG_VOTE_NEED_OPTIONS");
6856 int find_id = atoi(argv[1]);
6858 unsigned int found = 0;
6861 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6863 if (find_id == ii) {
6864 struct vote_option *vOpt = iter_data(it);
6865 found = vOpt->option_id;
6867 sprintf(str,"%i",vOpt->option_id);
6868 dict_remove(cData->vote_options, str);
6873 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6874 if(uData->votefor == found) {
6879 reply("CSMSG_DELOPTION_DONE");
6882 reply("CSMSG_DELOPTION_NONE");
6887 static CHANSERV_FUNC(cmd_vote)
6889 struct chanData *cData = channel->channel_info;
6890 struct userData *target;
6891 struct handle_info *hi;
6892 unsigned int votedfor = 0;
6893 char *votedfor_str = NULL;
6895 if (!cData || !cData->vote) {
6896 reply("CSMSG_NO_VOTE");
6899 if(argc > 1 && cData->vote_start) {
6900 hi = user->handle_info;
6901 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6903 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6906 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6907 reply("CSMSG_NO_ACCESS");
6911 reply("CSMSG_VOTE_VOTED");
6914 int find_id = atoi(argv[1]);
6917 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6919 if (find_id == ii) {
6920 struct vote_option *vOpt = iter_data(it);
6923 target->votefor = vOpt->option_id;
6924 votedfor = vOpt->option_id;
6925 votedfor_str = vOpt->name;
6929 reply("CSMSG_VOTE_INVALID");
6933 if (!cData->vote_start) {
6934 reply("CSMSG_VOTE_NOT_STARTED");
6936 reply("CSMSG_VOTE_QUESTION",cData->vote);
6938 unsigned int voteid = 0;
6941 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6942 struct vote_option *vOpt = iter_data(it);
6944 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6946 if(argc > 1 && cData->vote_start && votedfor_str) {
6947 reply("CSMSG_VOTE_DONE",votedfor_str);
6952 static CHANSERV_FUNC(cmd_startvote)
6954 struct chanData *cData = channel->channel_info;
6955 struct userData *target;
6956 struct handle_info *hi;
6957 if (!cData) return 0;
6958 hi = user->handle_info;
6959 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6961 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6964 if(target->access < 300) {
6965 reply("CSMSG_NO_ACCESS");
6969 reply("CSMSG_NO_VOTE");
6972 if(cData->vote_start) {
6973 reply("CSMSG_STARTVOTE_RUNNING");
6976 if(dict_size(cData->vote_options) < 2) {
6977 reply("CSMSG_VOTE_NEED_OPTIONS");
6980 cData->vote_start = 1;
6981 char response[MAXLEN];
6982 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6983 irc_privmsg(cmd->parent->bot, channel->name, response);
6984 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6985 irc_privmsg(cmd->parent->bot, channel->name, response);
6986 unsigned int voteid = 0;
6988 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6989 struct vote_option *vOpt = iter_data(it);
6991 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6992 irc_privmsg(cmd->parent->bot, channel->name, response);
6994 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6995 irc_privmsg(cmd->parent->bot, channel->name, response);
6996 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6997 irc_privmsg(cmd->parent->bot, channel->name, response);
7001 static CHANSERV_FUNC(cmd_endvote)
7003 struct chanData *cData = channel->channel_info;
7004 struct userData *target;
7005 struct handle_info *hi;
7006 if (!cData) return 0;
7007 hi = user->handle_info;
7008 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7010 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7013 if(target->access < 300) {
7014 reply("CSMSG_NO_ACCESS");
7018 reply("CSMSG_NO_VOTE");
7021 if(!cData->vote_start) {
7022 reply("CSMSG_ENDVOTE_STOPPED");
7025 cData->vote_start = 0;
7026 reply("CSMSG_ENDVOTE_DONE");
7030 static CHANSERV_FUNC(cmd_voteresults)
7032 struct chanData *cData = channel->channel_info;
7033 struct userData *target;
7034 struct handle_info *hi;
7035 if (!cData) return 0;
7037 reply("CSMSG_NO_VOTE");
7040 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7041 hi = user->handle_info;
7042 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7044 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7047 if(target->access < 300) {
7048 reply("CSMSG_NO_ACCESS");
7051 char response[MAXLEN];
7052 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7053 irc_privmsg(cmd->parent->bot, channel->name, response);
7054 unsigned int voteid = 0;
7056 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7057 struct vote_option *vOpt = iter_data(it);
7059 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7060 irc_privmsg(cmd->parent->bot, channel->name, response);
7063 reply("CSMSG_VOTE_QUESTION",cData->vote);
7064 unsigned int voteid = 0;
7066 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7067 struct vote_option *vOpt = iter_data(it);
7069 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7076 chanserv_refresh_topics(UNUSED_ARG(void *data))
7078 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7079 struct chanData *cData;
7082 for(cData = channelList; cData; cData = cData->next)
7084 if(IsSuspended(cData))
7086 opt = cData->chOpts[chTopicRefresh];
7089 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7092 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7093 cData->last_refresh = refresh_num;
7095 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7098 static CHANSERV_FUNC(cmd_unf)
7102 char response[MAXLEN];
7103 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7104 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7105 irc_privmsg(cmd->parent->bot, channel->name, response);
7108 reply("CSMSG_UNF_RESPONSE");
7112 static CHANSERV_FUNC(cmd_ping)
7116 char response[MAXLEN];
7117 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7118 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7119 irc_privmsg(cmd->parent->bot, channel->name, response);
7122 reply("CSMSG_PING_RESPONSE");
7126 static CHANSERV_FUNC(cmd_wut)
7130 char response[MAXLEN];
7131 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7132 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7133 irc_privmsg(cmd->parent->bot, channel->name, response);
7136 reply("CSMSG_WUT_RESPONSE");
7140 static CHANSERV_FUNC(cmd_8ball)
7142 unsigned int i, j, accum;
7147 for(i=1; i<argc; i++)
7148 for(j=0; argv[i][j]; j++)
7149 accum = (accum << 5) - accum + toupper(argv[i][j]);
7150 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7153 char response[MAXLEN];
7154 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7155 irc_privmsg(cmd->parent->bot, channel->name, response);
7158 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7162 static CHANSERV_FUNC(cmd_d)
7164 unsigned long sides, count, modifier, ii, total;
7165 char response[MAXLEN], *sep;
7169 if((count = strtoul(argv[1], &sep, 10)) < 1)
7179 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7180 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7184 else if((sep[0] == '-') && isdigit(sep[1]))
7185 modifier = strtoul(sep, NULL, 10);
7186 else if((sep[0] == '+') && isdigit(sep[1]))
7187 modifier = strtoul(sep+1, NULL, 10);
7194 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7199 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7202 for(total = ii = 0; ii < count; ++ii)
7203 total += (rand() % sides) + 1;
7206 if((count > 1) || modifier)
7208 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7209 sprintf(response, fmt, total, count, sides, modifier);
7213 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7214 sprintf(response, fmt, total, sides);
7217 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7219 send_message_type(4, user, cmd->parent->bot, "%s", response);
7223 static CHANSERV_FUNC(cmd_huggle)
7225 /* CTCP must be via PRIVMSG, never notice */
7227 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7229 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7234 chanserv_adjust_limit(void *data)
7236 struct mod_chanmode change;
7237 struct chanData *cData = data;
7238 struct chanNode *channel = cData->channel;
7241 if(IsSuspended(cData))
7244 cData->limitAdjusted = now;
7245 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7246 if(cData->modes.modes_set & MODE_LIMIT)
7248 if(limit > cData->modes.new_limit)
7249 limit = cData->modes.new_limit;
7250 else if(limit == cData->modes.new_limit)
7254 mod_chanmode_init(&change);
7255 change.modes_set = MODE_LIMIT;
7256 change.new_limit = limit;
7257 mod_chanmode_announce(chanserv, channel, &change);
7261 handle_new_channel(struct chanNode *channel)
7263 struct chanData *cData;
7265 if(!(cData = channel->channel_info))
7268 if(cData->modes.modes_set || cData->modes.modes_clear)
7269 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7271 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7272 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7275 void handle_new_channel_created(char *chan, struct userNode *user) {
7276 if(user->handle_info && chanserv_conf.new_channel_authed) {
7277 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7278 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7279 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7281 if(chanserv_conf.new_channel_msg)
7282 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7285 /* Welcome to my worst nightmare. Warning: Read (or modify)
7286 the code below at your own risk. */
7288 handle_join(struct modeNode *mNode)
7290 struct mod_chanmode change;
7291 struct userNode *user = mNode->user;
7292 struct chanNode *channel = mNode->channel;
7293 struct chanData *cData;
7294 struct userData *uData = NULL;
7295 struct banData *bData;
7296 struct handle_info *handle;
7297 unsigned int modes = 0, info = 0;
7301 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7304 cData = channel->channel_info;
7305 if(channel->members.used > cData->max) {
7306 cData->max = channel->members.used;
7307 cData->max_time = now;
7310 for(i = 0; i < channel->invited.used; i++)
7312 if(channel->invited.list[i] == user) {
7313 userList_remove(&channel->invited, user);
7317 /* Check for bans. If they're joining through a ban, one of two
7319 * 1: Join during a netburst, by riding the break. Kick them
7320 * unless they have ops or voice in the channel.
7321 * 2: They're allowed to join through the ban (an invite in
7322 * ircu2.10, or a +e on Hybrid, or something).
7323 * If they're not joining through a ban, and the banlist is not
7324 * full, see if they're on the banlist for the channel. If so,
7327 if(user->uplink->burst && !mNode->modes)
7330 for(ii = 0; ii < channel->banlist.used; ii++)
7332 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7334 /* Riding a netburst. Naughty. */
7335 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7341 mod_chanmode_init(&change);
7343 if(channel->banlist.used < MAXBANS)
7345 /* Not joining through a ban. */
7346 for(bData = cData->bans;
7347 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7348 bData = bData->next);
7352 char kick_reason[MAXLEN];
7353 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7355 bData->triggered = now;
7356 if(bData != cData->bans)
7358 /* Shuffle the ban to the head of the list. */
7360 bData->next->prev = bData->prev;
7362 bData->prev->next = bData->next;
7365 bData->next = cData->bans;
7368 cData->bans->prev = bData;
7369 cData->bans = bData;
7372 change.args[0].mode = MODE_BAN;
7373 change.args[0].u.hostmask = bData->mask;
7374 mod_chanmode_announce(chanserv, channel, &change);
7375 KickChannelUser(user, channel, chanserv, kick_reason);
7380 /* ChanServ will not modify the limits in join-flooded channels,
7381 or when there are enough slots left below the limit. */
7382 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7383 && !channel->join_flooded
7384 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7386 /* The user count has begun "bumping" into the channel limit,
7387 so set a timer to raise the limit a bit. Any previous
7388 timers are removed so three incoming users within the delay
7389 results in one limit change, not three. */
7391 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7392 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7395 if(channel->join_flooded)
7397 /* don't automatically give ops or voice during a join flood */
7399 else if(cData->lvlOpts[lvlGiveOps] == 0)
7400 modes |= MODE_CHANOP;
7401 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7402 modes |= MODE_VOICE;
7404 greeting = cData->greeting;
7405 if(user->handle_info)
7407 handle = user->handle_info;
7409 if(IsHelper(user) && !IsHelping(user))
7412 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7414 if(channel == chanserv_conf.support_channels.list[ii])
7416 HANDLE_SET_FLAG(user->handle_info, HELPING);
7422 uData = GetTrueChannelAccess(cData, handle);
7423 if(uData && !IsUserSuspended(uData))
7425 /* Ops and above were handled by the above case. */
7426 if(IsUserAutoOp(uData))
7428 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7429 modes |= MODE_CHANOP;
7430 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7431 modes |= MODE_VOICE;
7433 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7434 cData->visited = now;
7435 if(cData->user_greeting)
7436 greeting = cData->user_greeting;
7438 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7439 && ((now - uData->seen) >= chanserv_conf.info_delay)
7447 /* If user joining normally (not during burst), apply op or voice,
7448 * and send greeting/userinfo as appropriate.
7450 if(!user->uplink->burst)
7454 if(modes & MODE_CHANOP)
7455 modes &= ~MODE_VOICE;
7456 change.args[0].mode = modes;
7457 change.args[0].u.member = mNode;
7458 mod_chanmode_announce(chanserv, channel, &change);
7461 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7462 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7463 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7469 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7471 struct mod_chanmode change;
7472 struct userData *channel;
7473 unsigned int ii, jj;
7475 if(!user->handle_info)
7478 mod_chanmode_init(&change);
7480 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7482 struct chanNode *cn;
7483 struct modeNode *mn;
7484 if(IsUserSuspended(channel)
7485 || IsSuspended(channel->channel)
7486 || !(cn = channel->channel->channel))
7489 mn = GetUserMode(cn, user);
7492 if(!IsUserSuspended(channel)
7493 && IsUserAutoInvite(channel)
7494 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7496 && !user->uplink->burst)
7497 irc_invite(chanserv, user, cn);
7501 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7502 channel->channel->visited = now;
7504 if(IsUserAutoOp(channel))
7506 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7507 change.args[0].mode = MODE_CHANOP;
7508 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7509 change.args[0].mode = MODE_VOICE;
7511 change.args[0].mode = 0;
7512 change.args[0].u.member = mn;
7513 if(change.args[0].mode)
7514 mod_chanmode_announce(chanserv, cn, &change);
7517 channel->seen = now;
7518 channel->present = 1;
7521 for(ii = 0; ii < user->channels.used; ++ii)
7523 struct chanNode *chan = user->channels.list[ii]->channel;
7524 struct banData *ban;
7526 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7527 || !chan->channel_info
7528 || IsSuspended(chan->channel_info))
7530 for(jj = 0; jj < chan->banlist.used; ++jj)
7531 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7533 if(jj < chan->banlist.used)
7535 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7537 char kick_reason[MAXLEN];
7538 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7540 change.args[0].mode = MODE_BAN;
7541 change.args[0].u.hostmask = ban->mask;
7542 mod_chanmode_announce(chanserv, chan, &change);
7543 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7544 KickChannelUser(user, chan, chanserv, kick_reason);
7545 ban->triggered = now;
7550 if(IsSupportHelper(user))
7552 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7554 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7556 HANDLE_SET_FLAG(user->handle_info, HELPING);
7564 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7566 struct chanData *cData;
7567 struct userData *uData;
7569 cData = mn->channel->channel_info;
7570 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7573 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7575 /* Allow for a bit of padding so that the limit doesn't
7576 track the user count exactly, which could get annoying. */
7577 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7579 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7580 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7584 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7586 scan_user_presence(uData, mn->user);
7588 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7589 cData->visited = now;
7592 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7595 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7596 struct chanNode *channel;
7597 struct userNode *exclude;
7598 /* When looking at the channel that is being /part'ed, we
7599 * have to skip over the client that is leaving. For
7600 * other channels, we must not do that.
7602 channel = chanserv_conf.support_channels.list[ii];
7603 exclude = (channel == mn->channel) ? mn->user : NULL;
7604 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7607 if(ii == chanserv_conf.support_channels.used)
7608 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7613 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7615 struct userData *uData;
7617 if(!channel->channel_info || !kicker || IsService(kicker)
7618 || (kicker == victim) || IsSuspended(channel->channel_info)
7619 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7622 if(protect_user(victim, kicker, channel->channel_info))
7624 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7625 KickChannelUser(kicker, channel, chanserv, reason);
7628 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7633 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7635 struct chanData *cData;
7637 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7640 cData = channel->channel_info;
7641 if(bad_topic(channel, user, channel->topic))
7643 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7644 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7645 SetChannelTopic(channel, chanserv, old_topic, 1);
7646 else if(cData->topic)
7647 SetChannelTopic(channel, chanserv, cData->topic, 1);
7650 /* With topicsnarf, grab the topic and save it as the default topic. */
7651 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7654 cData->topic = strdup(channel->topic);
7660 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7662 struct mod_chanmode *bounce = NULL;
7663 unsigned int bnc, ii;
7666 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7669 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7670 && mode_lock_violated(&channel->channel_info->modes, change))
7672 char correct[MAXLEN];
7673 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7674 mod_chanmode_format(&channel->channel_info->modes, correct);
7675 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7677 for(ii = bnc = 0; ii < change->argc; ++ii)
7679 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7681 const struct userNode *victim = change->args[ii].u.member->user;
7682 if(!protect_user(victim, user, channel->channel_info))
7685 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7688 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7689 bounce->args[bnc].u.member = GetUserMode(channel, user);
7690 if(bounce->args[bnc].u.member)
7694 bounce->args[bnc].mode = MODE_CHANOP;
7695 bounce->args[bnc].u.member = change->args[ii].u.member;
7697 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7699 else if(change->args[ii].mode & MODE_CHANOP)
7701 const struct userNode *victim = change->args[ii].u.member->user;
7702 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7705 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7706 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7707 bounce->args[bnc].u.member = change->args[ii].u.member;
7710 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7712 const char *ban = change->args[ii].u.hostmask;
7713 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7716 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7717 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7718 bounce->args[bnc].u.hostmask = strdup(ban);
7720 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7725 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7726 mod_chanmode_announce(chanserv, channel, bounce);
7727 for(ii = 0; ii < change->argc; ++ii)
7728 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7729 free((char*)bounce->args[ii].u.hostmask);
7730 mod_chanmode_free(bounce);
7735 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7737 struct chanNode *channel;
7738 struct banData *bData;
7739 struct mod_chanmode change;
7740 unsigned int ii, jj;
7741 char kick_reason[MAXLEN];
7743 mod_chanmode_init(&change);
7745 change.args[0].mode = MODE_BAN;
7746 for(ii = 0; ii < user->channels.used; ++ii)
7748 channel = user->channels.list[ii]->channel;
7749 /* Need not check for bans if they're opped or voiced. */
7750 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7752 /* Need not check for bans unless channel registration is active. */
7753 if(!channel->channel_info || IsSuspended(channel->channel_info))
7755 /* Look for a matching ban already on the channel. */
7756 for(jj = 0; jj < channel->banlist.used; ++jj)
7757 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7759 /* Need not act if we found one. */
7760 if(jj < channel->banlist.used)
7762 /* Look for a matching ban in this channel. */
7763 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7765 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7767 change.args[0].u.hostmask = bData->mask;
7768 mod_chanmode_announce(chanserv, channel, &change);
7769 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7770 KickChannelUser(user, channel, chanserv, kick_reason);
7771 bData->triggered = now;
7772 break; /* we don't need to check any more bans in the channel */
7777 static void handle_rename(struct handle_info *handle, const char *old_handle)
7779 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7783 dict_remove2(handle_dnrs, old_handle, 1);
7784 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7785 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7790 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7792 struct userNode *h_user;
7794 if(handle->channels)
7796 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7797 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7799 while(handle->channels)
7800 del_channel_user(handle->channels, 1);
7805 handle_server_link(UNUSED_ARG(struct server *server))
7807 struct chanData *cData;
7809 for(cData = channelList; cData; cData = cData->next)
7811 if(!IsSuspended(cData))
7812 cData->may_opchan = 1;
7813 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7814 && !cData->channel->join_flooded
7815 && ((cData->channel->limit - cData->channel->members.used)
7816 < chanserv_conf.adjust_threshold))
7818 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7819 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7825 chanserv_conf_read(void)
7829 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7830 struct mod_chanmode *change;
7831 struct string_list *strlist;
7832 struct chanNode *chan;
7835 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7837 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7840 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7841 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7842 chanserv_conf.support_channels.used = 0;
7843 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7845 for(ii = 0; ii < strlist->used; ++ii)
7847 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7850 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7852 channelList_append(&chanserv_conf.support_channels, chan);
7855 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7858 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7861 chan = AddChannel(str, now, str2, NULL);
7863 channelList_append(&chanserv_conf.support_channels, chan);
7865 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7866 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7867 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7868 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7869 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7870 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7871 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7872 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7873 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7874 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7875 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7876 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7877 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7878 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7879 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7880 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7881 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7882 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7883 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7884 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7885 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7886 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7887 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7888 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7889 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7890 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7891 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7892 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7893 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7894 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7895 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7897 NickChange(chanserv, str, 0);
7898 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7899 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7900 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7901 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7902 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7903 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7904 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7905 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7906 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7907 chanserv_conf.max_owned = str ? atoi(str) : 5;
7908 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7909 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7910 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7911 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7912 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7913 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7914 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7915 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7916 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7917 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7918 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7919 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7920 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7923 safestrncpy(mode_line, str, sizeof(mode_line));
7924 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7925 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7926 && (change->argc < 2))
7928 chanserv_conf.default_modes = *change;
7929 mod_chanmode_free(change);
7931 free_string_list(chanserv_conf.set_shows);
7932 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7934 strlist = string_list_copy(strlist);
7937 static const char *list[] = {
7938 /* free form text */
7939 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7940 /* options based on user level */
7941 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7942 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7943 /* multiple choice options */
7944 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7945 /* binary options */
7946 "DynLimit", "NoDelete", "expire", "Vote",
7950 strlist = alloc_string_list(ArrayLength(list)-1);
7951 for(ii=0; list[ii]; ii++)
7952 string_list_append(strlist, strdup(list[ii]));
7954 chanserv_conf.set_shows = strlist;
7955 /* We don't look things up now, in case the list refers to options
7956 * defined by modules initialized after this point. Just mark the
7957 * function list as invalid, so it will be initialized.
7959 set_shows_list.used = 0;
7960 free_string_list(chanserv_conf.eightball);
7961 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7964 strlist = string_list_copy(strlist);
7968 strlist = alloc_string_list(4);
7969 string_list_append(strlist, strdup("Yes."));
7970 string_list_append(strlist, strdup("No."));
7971 string_list_append(strlist, strdup("Maybe so."));
7973 chanserv_conf.eightball = strlist;
7974 free_string_list(chanserv_conf.old_ban_names);
7975 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7977 strlist = string_list_copy(strlist);
7979 strlist = alloc_string_list(2);
7980 chanserv_conf.old_ban_names = strlist;
7981 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7982 off_channel = str ? atoi(str) : 0;
7986 chanserv_note_type_read(const char *key, struct record_data *rd)
7989 struct note_type *ntype;
7992 if(!(obj = GET_RECORD_OBJECT(rd)))
7994 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7997 if(!(ntype = chanserv_create_note_type(key)))
7999 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8003 /* Figure out set access */
8004 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8006 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8007 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8009 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8011 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8012 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8014 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8016 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8020 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8021 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8022 ntype->set_access.min_opserv = 0;
8025 /* Figure out visibility */
8026 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8027 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8028 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8029 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8030 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8031 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8032 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8033 ntype->visible_type = NOTE_VIS_ALL;
8035 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8037 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8038 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8042 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8044 struct vote_option *vOpt;
8047 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8049 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8053 vOpt = calloc(1, sizeof(*vOpt));
8054 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8055 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8056 vOpt->voted = str ? atoi(str) : 0;
8057 vOpt->option_id = str ? atoi(key) : 0;
8058 vOpt->option_str = strdup(key);
8059 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8063 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8065 struct handle_info *handle;
8066 struct userData *uData;
8067 char *seen, *inf, *flags, *voted, *votefor, *expires;
8068 unsigned long last_seen;
8069 unsigned short access_level;
8071 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8073 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8077 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8078 if(access_level > UL_OWNER)
8080 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8084 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8085 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8086 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8087 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8088 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8089 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8090 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8091 handle = get_handle_info(key);
8094 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8098 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8099 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8100 uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8102 if((uData->flags & USER_SUSPENDED) && uData->expires)
8104 if(uData->expires > now)
8105 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8107 uData->flags &= ~USER_SUSPENDED;
8110 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8111 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8119 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8121 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8122 unsigned long set_time, triggered_time, expires_time;
8124 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8126 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8130 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8131 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8132 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8133 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8134 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8135 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8136 if (!reason || !owner)
8139 set_time = set ? strtoul(set, NULL, 0) : now;
8140 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8142 expires_time = strtoul(s_expires, NULL, 0);
8144 expires_time = set_time + atoi(s_duration);
8148 if(!reason || (expires_time && (expires_time < now)))
8151 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8154 static struct suspended *
8155 chanserv_read_suspended(dict_t obj)
8157 struct suspended *suspended = calloc(1, sizeof(*suspended));
8161 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8162 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8163 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8164 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8165 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8166 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8167 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8168 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8169 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8170 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8174 static struct giveownership *
8175 chanserv_read_giveownership(dict_t obj)
8177 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8181 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8182 giveownership->staff_issuer = str ? strdup(str) : NULL;
8184 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8186 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8187 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8189 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8190 giveownership->reason = str ? strdup(str) : NULL;
8191 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8192 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8194 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8195 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8196 return giveownership;
8200 chanserv_channel_read(const char *key, struct record_data *hir)
8202 struct suspended *suspended;
8203 struct giveownership *giveownership;
8204 struct mod_chanmode *modes;
8205 struct chanNode *cNode;
8206 struct chanData *cData;
8207 struct dict *channel, *obj;
8208 char *str, *argv[10];
8212 channel = hir->d.object;
8214 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8217 cNode = AddChannel(key, now, NULL, NULL);
8220 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8223 cData = register_channel(cNode, str);
8226 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8230 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8232 enum levelOption lvlOpt;
8233 enum charOption chOpt;
8235 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8236 cData->flags = atoi(str);
8238 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8240 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8242 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8243 else if(levelOptions[lvlOpt].old_flag)
8245 if(cData->flags & levelOptions[lvlOpt].old_flag)
8246 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8248 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8252 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8254 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8256 cData->chOpts[chOpt] = str[0];
8259 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8261 enum levelOption lvlOpt;
8262 enum charOption chOpt;
8265 cData->flags = base64toint(str, 5);
8266 count = strlen(str += 5);
8267 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8270 if(levelOptions[lvlOpt].old_flag)
8272 if(cData->flags & levelOptions[lvlOpt].old_flag)
8273 lvl = levelOptions[lvlOpt].flag_value;
8275 lvl = levelOptions[lvlOpt].default_value;
8277 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8279 case 'c': lvl = UL_COOWNER; break;
8280 case 'm': lvl = UL_MASTER; break;
8281 case 'n': lvl = UL_OWNER+1; break;
8282 case 'o': lvl = UL_OP; break;
8283 case 'p': lvl = UL_PEON; break;
8284 case 'w': lvl = UL_OWNER; break;
8285 default: lvl = 0; break;
8287 cData->lvlOpts[lvlOpt] = lvl;
8289 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8290 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8293 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8295 cData->expiry = atoi(str);
8296 if(cData->expiry > 0) {
8297 if(cData->expiry > now) {
8298 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8300 timeq_add(1, chanserv_expire_channel, cData);
8307 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8309 suspended = chanserv_read_suspended(obj);
8310 cData->suspended = suspended;
8311 suspended->cData = cData;
8312 /* We could use suspended->expires and suspended->revoked to
8313 * set the CHANNEL_SUSPENDED flag, but we don't. */
8315 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8317 suspended = calloc(1, sizeof(*suspended));
8318 suspended->issued = 0;
8319 suspended->revoked = 0;
8320 suspended->suspender = strdup(str);
8321 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8322 suspended->expires = str ? atoi(str) : 0;
8323 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8324 suspended->reason = strdup(str ? str : "No reason");
8325 suspended->previous = NULL;
8326 cData->suspended = suspended;
8327 suspended->cData = cData;
8331 cData->flags &= ~CHANNEL_SUSPENDED;
8332 suspended = NULL; /* to squelch a warning */
8335 if(IsSuspended(cData)) {
8336 if(suspended->expires > now)
8337 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8338 else if(suspended->expires)
8339 cData->flags &= ~CHANNEL_SUSPENDED;
8342 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8344 giveownership = chanserv_read_giveownership(obj);
8345 cData->giveownership = giveownership;
8348 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8349 struct mod_chanmode change;
8350 mod_chanmode_init(&change);
8352 change.args[0].mode = MODE_CHANOP;
8353 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8354 mod_chanmode_announce(chanserv, cNode, &change);
8357 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8358 cData->registered = str ? strtoul(str, NULL, 0) : now;
8359 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8360 cData->visited = str ? strtoul(str, NULL, 0) : now;
8361 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8362 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8363 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8364 cData->max = str ? atoi(str) : 0;
8365 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8366 cData->max_time = str ? atoi(str) : 0;
8367 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8368 cData->greeting = str ? strdup(str) : NULL;
8369 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8370 cData->user_greeting = str ? strdup(str) : NULL;
8371 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8372 cData->topic_mask = str ? strdup(str) : NULL;
8373 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8374 cData->topic = str ? strdup(str) : NULL;
8376 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8378 cData->vote = str ? strdup(str) : NULL;
8379 dict_delete(cData->vote_options);
8380 cData->vote_options = dict_new();
8381 dict_set_free_data(cData->vote_options, free_vote_options);
8382 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8383 cData->vote_start = str ? atoi(str) : 0;
8384 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8385 for(it = dict_first(obj); it; it = iter_next(it)) {
8386 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8390 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8391 for(it = dict_first(obj); it; it = iter_next(it))
8393 struct record_data *rd = iter_data(it);
8394 if(rd->type != RECDB_QSTRING) continue;
8395 int advtopic_index = atoi(iter_key(it));
8396 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8397 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8400 if(!IsSuspended(cData)
8401 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8402 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8403 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8404 cData->modes = *modes;
8406 cData->modes.modes_set |= MODE_REGISTERED;
8407 if(cData->modes.argc > 1)
8408 cData->modes.argc = 1;
8409 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8410 mod_chanmode_free(modes);
8413 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8414 for(it = dict_first(obj); it; it = iter_next(it))
8415 user_read_helper(iter_key(it), iter_data(it), cData);
8417 if(!cData->users && !IsProtected(cData))
8419 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8420 unregister_channel(cData, "has empty user list.");
8424 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8425 for(it = dict_first(obj); it; it = iter_next(it))
8426 ban_read_helper(iter_key(it), iter_data(it), cData);
8428 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8429 for(it = dict_first(obj); it; it = iter_next(it))
8431 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8432 struct record_data *rd = iter_data(it);
8433 const char *note, *setter;
8435 if(rd->type != RECDB_OBJECT)
8437 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8441 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8443 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8445 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8449 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8450 if(!setter) setter = "<unknown>";
8451 chanserv_add_channel_note(cData, ntype, setter, note);
8459 chanserv_dnr_read(const char *key, struct record_data *hir)
8461 const char *setter, *reason, *str;
8462 struct do_not_register *dnr;
8463 unsigned long expiry;
8465 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8468 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8471 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8474 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8477 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8478 expiry = str ? strtoul(str, NULL, 0) : 0;
8479 if(expiry && expiry <= now)
8481 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8484 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8486 dnr->set = atoi(str);
8492 chanserv_saxdb_read(struct dict *database)
8494 struct dict *section;
8497 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8498 for(it = dict_first(section); it; it = iter_next(it))
8499 chanserv_note_type_read(iter_key(it), iter_data(it));
8501 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8502 for(it = dict_first(section); it; it = iter_next(it))
8503 chanserv_channel_read(iter_key(it), iter_data(it));
8505 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8506 for(it = dict_first(section); it; it = iter_next(it))
8507 chanserv_dnr_read(iter_key(it), iter_data(it));
8513 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8515 int high_present = 0;
8516 saxdb_start_record(ctx, KEY_USERS, 1);
8517 for(; uData; uData = uData->next)
8519 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8521 saxdb_start_record(ctx, uData->handle->handle, 0);
8522 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8523 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8525 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8527 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8528 if(uData->channel->vote && uData->voted)
8529 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8530 if(uData->channel->vote && uData->votefor)
8531 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8533 saxdb_write_string(ctx, KEY_INFO, uData->info);
8534 saxdb_end_record(ctx);
8536 saxdb_end_record(ctx);
8537 return high_present;
8541 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8545 saxdb_start_record(ctx, KEY_BANS, 1);
8546 for(; bData; bData = bData->next)
8548 saxdb_start_record(ctx, bData->mask, 0);
8549 saxdb_write_int(ctx, KEY_SET, bData->set);
8550 if(bData->triggered)
8551 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8553 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8555 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8557 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8558 saxdb_end_record(ctx);
8560 saxdb_end_record(ctx);
8564 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8566 saxdb_start_record(ctx, name, 0);
8567 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8568 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8570 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8572 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8574 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8576 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8577 saxdb_end_record(ctx);
8581 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8583 saxdb_start_record(ctx, name, 0);
8584 if(giveownership->staff_issuer)
8585 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8586 if(giveownership->old_owner)
8587 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8588 if(giveownership->target)
8589 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8590 if(giveownership->target_access)
8591 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8592 if(giveownership->reason)
8593 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8594 if(giveownership->issued)
8595 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8596 if(giveownership->previous)
8597 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8598 saxdb_end_record(ctx);
8602 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8606 enum levelOption lvlOpt;
8607 enum charOption chOpt;
8610 saxdb_start_record(ctx, channel->channel->name, 1);
8612 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8613 saxdb_write_int(ctx, KEY_MAX, channel->max);
8614 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8616 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8617 if(channel->registrar)
8618 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8619 if(channel->greeting)
8620 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8621 if(channel->user_greeting)
8622 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8623 if(channel->topic_mask)
8624 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8625 if(channel->suspended)
8626 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8627 if(channel->giveownership)
8628 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8630 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8633 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8634 if(channel->vote_start)
8635 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8636 if (dict_size(channel->vote_options)) {
8637 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8638 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8639 struct vote_option *vOpt = iter_data(it);
8641 sprintf(str,"%i",vOpt->option_id);
8642 saxdb_start_record(ctx, str, 0);
8644 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8646 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8647 saxdb_end_record(ctx);
8649 saxdb_end_record(ctx);
8653 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8654 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8655 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8656 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8657 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8659 buf[0] = channel->chOpts[chOpt];
8661 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8663 saxdb_end_record(ctx);
8665 if(channel->modes.modes_set || channel->modes.modes_clear)
8667 mod_chanmode_format(&channel->modes, buf);
8668 saxdb_write_string(ctx, KEY_MODES, buf);
8671 high_present = chanserv_write_users(ctx, channel->users);
8672 chanserv_write_bans(ctx, channel->bans);
8674 if(channel->flags & CHANNEL_ADVTOPIC) {
8675 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8677 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8678 if(channel->advtopic[advtopic_index])
8679 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8681 saxdb_end_record(ctx);
8684 if(dict_size(channel->notes))
8688 saxdb_start_record(ctx, KEY_NOTES, 1);
8689 for(it = dict_first(channel->notes); it; it = iter_next(it))
8691 struct note *note = iter_data(it);
8692 saxdb_start_record(ctx, iter_key(it), 0);
8693 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8694 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8695 saxdb_end_record(ctx);
8697 saxdb_end_record(ctx);
8700 if(channel->ownerTransfer)
8701 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8702 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8703 saxdb_end_record(ctx);
8707 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8711 saxdb_start_record(ctx, ntype->name, 0);
8712 switch(ntype->set_access_type)
8714 case NOTE_SET_CHANNEL_ACCESS:
8715 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8717 case NOTE_SET_CHANNEL_SETTER:
8718 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8720 case NOTE_SET_PRIVILEGED: default:
8721 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8724 switch(ntype->visible_type)
8726 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8727 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8728 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8730 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8731 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8732 saxdb_end_record(ctx);
8736 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8738 struct do_not_register *dnr;
8739 dict_iterator_t it, next;
8741 for(it = dict_first(dnrs); it; it = next)
8743 next = iter_next(it);
8744 dnr = iter_data(it);
8745 if(dnr->expires && dnr->expires <= now)
8747 dict_remove(dnrs, iter_key(it));
8750 saxdb_start_record(ctx, dnr->chan_name, 0);
8752 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8754 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8755 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8756 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8757 saxdb_end_record(ctx);
8762 chanserv_saxdb_write(struct saxdb_context *ctx)
8765 struct chanData *channel;
8768 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8769 for(it = dict_first(note_types); it; it = iter_next(it))
8770 chanserv_write_note_type(ctx, iter_data(it));
8771 saxdb_end_record(ctx);
8774 saxdb_start_record(ctx, KEY_DNR, 1);
8775 write_dnrs_helper(ctx, handle_dnrs);
8776 write_dnrs_helper(ctx, plain_dnrs);
8777 write_dnrs_helper(ctx, mask_dnrs);
8778 saxdb_end_record(ctx);
8781 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8782 for(channel = channelList; channel; channel = channel->next)
8783 chanserv_write_channel(ctx, channel);
8784 saxdb_end_record(ctx);
8790 chanserv_db_cleanup(void) {
8792 unreg_part_func(handle_part);
8794 unregister_channel(channelList, "terminating.");
8795 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8796 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8797 free(chanserv_conf.support_channels.list);
8798 dict_delete(handle_dnrs);
8799 dict_delete(plain_dnrs);
8800 dict_delete(mask_dnrs);
8801 dict_delete(note_types);
8802 free_string_list(chanserv_conf.eightball);
8803 free_string_list(chanserv_conf.old_ban_names);
8804 free_string_list(chanserv_conf.set_shows);
8805 free(set_shows_list.list);
8806 free(uset_shows_list.list);
8809 struct userData *helper = helperList;
8810 helperList = helperList->next;
8815 #if defined(GCC_VARMACROS)
8816 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8817 #elif defined(C99_VARMACROS)
8818 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8820 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8821 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8824 init_chanserv(const char *nick)
8826 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8827 conf_register_reload(chanserv_conf_read);
8831 reg_server_link_func(handle_server_link);
8832 reg_new_channel_func(handle_new_channel);
8833 reg_join_func(handle_join);
8834 reg_part_func(handle_part);
8835 reg_kick_func(handle_kick);
8836 reg_topic_func(handle_topic);
8837 reg_mode_change_func(handle_mode);
8838 reg_nick_change_func(handle_nick_change);
8839 reg_auth_func(handle_auth);
8842 reg_handle_rename_func(handle_rename);
8843 reg_unreg_func(handle_unreg);
8845 handle_dnrs = dict_new();
8846 dict_set_free_data(handle_dnrs, free);
8847 plain_dnrs = dict_new();
8848 dict_set_free_data(plain_dnrs, free);
8849 mask_dnrs = dict_new();
8850 dict_set_free_data(mask_dnrs, free);
8852 reg_svccmd_unbind_func(handle_svccmd_unbind);
8853 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8854 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8855 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8856 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8857 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8858 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8859 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8860 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8861 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8862 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8863 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8864 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8865 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8867 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8868 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8870 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8871 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8872 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8873 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8874 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8876 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8877 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8878 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8879 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8880 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8882 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8883 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8884 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8885 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8887 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8888 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8889 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8890 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8891 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8892 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8893 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8894 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8896 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8897 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8898 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8899 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8900 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8901 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8902 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8903 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8904 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8905 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8906 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8907 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8908 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8909 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8910 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8912 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8913 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8914 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8915 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8916 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8918 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8919 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8921 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8922 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8923 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8924 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8925 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8926 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8927 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8928 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8929 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8930 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8931 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8933 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8934 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8936 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8937 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8938 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8939 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8941 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8942 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8943 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8944 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8945 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8947 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8948 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8949 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8950 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8951 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8952 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8954 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8955 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8956 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8957 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8958 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8959 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8960 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8961 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8963 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8965 /* Channel options */
8966 DEFINE_CHANNEL_OPTION(defaulttopic);
8967 DEFINE_CHANNEL_OPTION(topicmask);
8968 DEFINE_CHANNEL_OPTION(greeting);
8969 DEFINE_CHANNEL_OPTION(usergreeting);
8970 DEFINE_CHANNEL_OPTION(modes);
8971 DEFINE_CHANNEL_OPTION(enfops);
8972 DEFINE_CHANNEL_OPTION(giveops);
8973 DEFINE_CHANNEL_OPTION(protect);
8974 DEFINE_CHANNEL_OPTION(enfmodes);
8975 DEFINE_CHANNEL_OPTION(enftopic);
8976 DEFINE_CHANNEL_OPTION(pubcmd);
8977 DEFINE_CHANNEL_OPTION(givevoice);
8978 DEFINE_CHANNEL_OPTION(userinfo);
8979 DEFINE_CHANNEL_OPTION(dynlimit);
8980 DEFINE_CHANNEL_OPTION(topicsnarf);
8981 DEFINE_CHANNEL_OPTION(vote);
8982 DEFINE_CHANNEL_OPTION(nodelete);
8983 DEFINE_CHANNEL_OPTION(toys);
8984 DEFINE_CHANNEL_OPTION(setters);
8985 DEFINE_CHANNEL_OPTION(topicrefresh);
8986 DEFINE_CHANNEL_OPTION(ctcpusers);
8987 DEFINE_CHANNEL_OPTION(ctcpreaction);
8988 DEFINE_CHANNEL_OPTION(inviteme);
8989 DEFINE_CHANNEL_OPTION(advtopic);
8990 DEFINE_CHANNEL_OPTION(unreviewed);
8991 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8992 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8993 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8995 DEFINE_CHANNEL_OPTION(offchannel);
8996 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8998 /* Alias set topic to set defaulttopic for compatibility. */
8999 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9002 DEFINE_USER_OPTION(noautoop);
9003 DEFINE_USER_OPTION(autoinvite);
9004 DEFINE_USER_OPTION(info);
9006 /* Alias uset autovoice to uset autoop. */
9007 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9009 note_types = dict_new();
9010 dict_set_free_data(note_types, chanserv_deref_note_type);
9013 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9014 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9015 service_register(chanserv)->trigger = '!';
9016 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9018 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9020 if(chanserv_conf.channel_expire_frequency)
9021 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9023 if(chanserv_conf.dnr_expire_frequency)
9024 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9026 if(chanserv_conf.refresh_period)
9028 unsigned long next_refresh;
9029 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9030 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9033 reg_exit_func(chanserv_db_cleanup);
9034 message_register_table(msgtab);