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);
6586 static CHANSERV_FUNC(cmd_suspend)
6588 struct handle_info *hi;
6589 struct userData *actor, *real_actor, *target;
6590 unsigned int override = 0;
6593 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6594 actor = GetChannelUser(channel->channel_info, user->handle_info);
6595 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6596 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6598 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6601 if(target->access >= actor->access)
6603 reply("MSG_USER_OUTRANKED", hi->handle);
6606 if(target->flags & USER_SUSPENDED)
6608 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6613 target->present = 0;
6616 if(!real_actor || target->access >= real_actor->access)
6617 override = CMD_LOG_OVERRIDE;
6618 target->flags |= USER_SUSPENDED;
6619 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6620 return 1 | override;
6623 static CHANSERV_FUNC(cmd_unsuspend)
6625 struct handle_info *hi;
6626 struct userData *actor, *real_actor, *target;
6627 unsigned int override = 0;
6630 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6631 actor = GetChannelUser(channel->channel_info, user->handle_info);
6632 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6633 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6635 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6638 if(target->access >= actor->access)
6640 reply("MSG_USER_OUTRANKED", hi->handle);
6643 if(!(target->flags & USER_SUSPENDED))
6645 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6648 if(!real_actor || target->access >= real_actor->access)
6649 override = CMD_LOG_OVERRIDE;
6650 target->flags &= ~USER_SUSPENDED;
6651 scan_user_presence(target, NULL);
6652 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6653 return 1 | override;
6656 static MODCMD_FUNC(cmd_deleteme)
6658 struct handle_info *hi;
6659 struct userData *target;
6660 const char *confirm_string;
6661 unsigned short access_level;
6664 hi = user->handle_info;
6665 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6667 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6670 if(target->access == UL_OWNER)
6672 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6675 confirm_string = make_confirmation_string(target);
6676 if((argc < 2) || strcmp(argv[1], confirm_string))
6678 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6681 access_level = target->access;
6682 channel_name = strdup(channel->name);
6683 del_channel_user(target, 1);
6684 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6689 static CHANSERV_FUNC(cmd_addvote)
6691 struct chanData *cData = channel->channel_info;
6692 struct userData *uData, *target;
6693 struct handle_info *hi;
6694 if (!cData) return 0;
6696 hi = user->handle_info;
6697 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6699 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6702 if(target->access < 300) {
6703 reply("CSMSG_NO_ACCESS");
6707 reply("CSMSG_ADDVOTE_FULL");
6711 msg = unsplit_string(argv + 1, argc - 1, NULL);
6712 cData->vote = strdup(msg);
6713 cData->vote_start=0;
6714 dict_delete(cData->vote_options);
6715 cData->vote_options = dict_new();
6716 dict_set_free_data(cData->vote_options, free_vote_options);
6717 for(uData = channel->channel_info->users; uData; uData = uData->next)
6722 reply("CSMSG_ADDVOTE_DONE");
6726 static CHANSERV_FUNC(cmd_delvote)
6728 struct chanData *cData = channel->channel_info;
6729 struct userData *target;
6730 struct handle_info *hi;
6731 if (!cData) return 0;
6732 hi = user->handle_info;
6733 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6735 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6738 if(target->access < 300) {
6739 reply("CSMSG_NO_ACCESS");
6743 reply("CSMSG_NO_VOTE");
6748 reply("CSMSG_DELVOTE_DONE");
6752 static CHANSERV_FUNC(cmd_addoption)
6754 struct chanData *cData = channel->channel_info;
6755 struct userData *target;
6756 struct handle_info *hi;
6757 if (!cData) return 0;
6759 hi = user->handle_info;
6760 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6762 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6765 if(target->access < 300) {
6766 reply("CSMSG_NO_ACCESS");
6770 reply("CSMSG_NO_VOTE");
6776 msg = unsplit_string(argv + 1, argc - 1, NULL);
6779 unsigned int lastid = 1;
6780 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6781 struct vote_option *cvOpt = iter_data(it);
6782 if(cvOpt->option_id > lastid)
6783 lastid = cvOpt->option_id;
6785 struct vote_option *vOpt;
6786 vOpt = calloc(1, sizeof(*vOpt));
6787 vOpt->name = strdup(msg);
6788 vOpt->option_id = (lastid + 1);
6790 sprintf(str,"%i",(lastid + 1));
6791 vOpt->option_str = strdup(str);
6793 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6795 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6799 static CHANSERV_FUNC(cmd_deloption)
6801 struct chanData *cData = channel->channel_info;
6802 struct userData *uData, *target;
6803 struct handle_info *hi;
6804 if (!cData) return 0;
6806 hi = user->handle_info;
6807 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6809 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6812 if(target->access < 300) {
6813 reply("CSMSG_NO_ACCESS");
6817 reply("CSMSG_NO_VOTE");
6820 if(cData->vote_start) {
6821 if(dict_size(cData->vote_options) < 3) {
6822 reply("CSMSG_VOTE_NEED_OPTIONS");
6827 int find_id = atoi(argv[1]);
6829 unsigned int found = 0;
6832 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6834 if (find_id == ii) {
6835 struct vote_option *vOpt = iter_data(it);
6836 found = vOpt->option_id;
6838 sprintf(str,"%i",vOpt->option_id);
6839 dict_remove(cData->vote_options, str);
6844 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6845 if(uData->votefor == found) {
6850 reply("CSMSG_DELOPTION_DONE");
6853 reply("CSMSG_DELOPTION_NONE");
6858 static CHANSERV_FUNC(cmd_vote)
6860 struct chanData *cData = channel->channel_info;
6861 struct userData *target;
6862 struct handle_info *hi;
6863 unsigned int votedfor = 0;
6864 char *votedfor_str = NULL;
6866 if (!cData || !cData->vote) {
6867 reply("CSMSG_NO_VOTE");
6870 if(argc > 1 && cData->vote_start) {
6871 hi = user->handle_info;
6872 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6874 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6877 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6878 reply("CSMSG_NO_ACCESS");
6882 reply("CSMSG_VOTE_VOTED");
6885 int find_id = atoi(argv[1]);
6888 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6890 if (find_id == ii) {
6891 struct vote_option *vOpt = iter_data(it);
6894 target->votefor = vOpt->option_id;
6895 votedfor = vOpt->option_id;
6896 votedfor_str = vOpt->name;
6900 reply("CSMSG_VOTE_INVALID");
6904 if (!cData->vote_start) {
6905 reply("CSMSG_VOTE_NOT_STARTED");
6907 reply("CSMSG_VOTE_QUESTION",cData->vote);
6909 unsigned int voteid = 0;
6912 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6913 struct vote_option *vOpt = iter_data(it);
6915 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6917 if(argc > 1 && cData->vote_start && votedfor_str) {
6918 reply("CSMSG_VOTE_DONE",votedfor_str);
6923 static CHANSERV_FUNC(cmd_startvote)
6925 struct chanData *cData = channel->channel_info;
6926 struct userData *target;
6927 struct handle_info *hi;
6928 if (!cData) return 0;
6929 hi = user->handle_info;
6930 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6932 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6935 if(target->access < 300) {
6936 reply("CSMSG_NO_ACCESS");
6940 reply("CSMSG_NO_VOTE");
6943 if(cData->vote_start) {
6944 reply("CSMSG_STARTVOTE_RUNNING");
6947 if(dict_size(cData->vote_options) < 2) {
6948 reply("CSMSG_VOTE_NEED_OPTIONS");
6951 cData->vote_start = 1;
6952 char response[MAXLEN];
6953 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6954 irc_privmsg(cmd->parent->bot, channel->name, response);
6955 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6956 irc_privmsg(cmd->parent->bot, channel->name, response);
6957 unsigned int voteid = 0;
6959 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6960 struct vote_option *vOpt = iter_data(it);
6962 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6963 irc_privmsg(cmd->parent->bot, channel->name, response);
6965 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6966 irc_privmsg(cmd->parent->bot, channel->name, response);
6967 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6968 irc_privmsg(cmd->parent->bot, channel->name, response);
6972 static CHANSERV_FUNC(cmd_endvote)
6974 struct chanData *cData = channel->channel_info;
6975 struct userData *target;
6976 struct handle_info *hi;
6977 if (!cData) return 0;
6978 hi = user->handle_info;
6979 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6981 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6984 if(target->access < 300) {
6985 reply("CSMSG_NO_ACCESS");
6989 reply("CSMSG_NO_VOTE");
6992 if(!cData->vote_start) {
6993 reply("CSMSG_ENDVOTE_STOPPED");
6996 cData->vote_start = 0;
6997 reply("CSMSG_ENDVOTE_DONE");
7001 static CHANSERV_FUNC(cmd_voteresults)
7003 struct chanData *cData = channel->channel_info;
7004 struct userData *target;
7005 struct handle_info *hi;
7006 if (!cData) return 0;
7008 reply("CSMSG_NO_VOTE");
7011 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7012 hi = user->handle_info;
7013 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7015 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7018 if(target->access < 300) {
7019 reply("CSMSG_NO_ACCESS");
7022 char response[MAXLEN];
7023 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7024 irc_privmsg(cmd->parent->bot, channel->name, response);
7025 unsigned int voteid = 0;
7027 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7028 struct vote_option *vOpt = iter_data(it);
7030 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7031 irc_privmsg(cmd->parent->bot, channel->name, response);
7034 reply("CSMSG_VOTE_QUESTION",cData->vote);
7035 unsigned int voteid = 0;
7037 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7038 struct vote_option *vOpt = iter_data(it);
7040 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7047 chanserv_refresh_topics(UNUSED_ARG(void *data))
7049 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7050 struct chanData *cData;
7053 for(cData = channelList; cData; cData = cData->next)
7055 if(IsSuspended(cData))
7057 opt = cData->chOpts[chTopicRefresh];
7060 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7063 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7064 cData->last_refresh = refresh_num;
7066 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7069 static CHANSERV_FUNC(cmd_unf)
7073 char response[MAXLEN];
7074 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7075 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7076 irc_privmsg(cmd->parent->bot, channel->name, response);
7079 reply("CSMSG_UNF_RESPONSE");
7083 static CHANSERV_FUNC(cmd_ping)
7087 char response[MAXLEN];
7088 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7089 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7090 irc_privmsg(cmd->parent->bot, channel->name, response);
7093 reply("CSMSG_PING_RESPONSE");
7097 static CHANSERV_FUNC(cmd_wut)
7101 char response[MAXLEN];
7102 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7103 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7104 irc_privmsg(cmd->parent->bot, channel->name, response);
7107 reply("CSMSG_WUT_RESPONSE");
7111 static CHANSERV_FUNC(cmd_8ball)
7113 unsigned int i, j, accum;
7118 for(i=1; i<argc; i++)
7119 for(j=0; argv[i][j]; j++)
7120 accum = (accum << 5) - accum + toupper(argv[i][j]);
7121 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7124 char response[MAXLEN];
7125 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7126 irc_privmsg(cmd->parent->bot, channel->name, response);
7129 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7133 static CHANSERV_FUNC(cmd_d)
7135 unsigned long sides, count, modifier, ii, total;
7136 char response[MAXLEN], *sep;
7140 if((count = strtoul(argv[1], &sep, 10)) < 1)
7150 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7151 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7155 else if((sep[0] == '-') && isdigit(sep[1]))
7156 modifier = strtoul(sep, NULL, 10);
7157 else if((sep[0] == '+') && isdigit(sep[1]))
7158 modifier = strtoul(sep+1, NULL, 10);
7165 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7170 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7173 for(total = ii = 0; ii < count; ++ii)
7174 total += (rand() % sides) + 1;
7177 if((count > 1) || modifier)
7179 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7180 sprintf(response, fmt, total, count, sides, modifier);
7184 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7185 sprintf(response, fmt, total, sides);
7188 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7190 send_message_type(4, user, cmd->parent->bot, "%s", response);
7194 static CHANSERV_FUNC(cmd_huggle)
7196 /* CTCP must be via PRIVMSG, never notice */
7198 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7200 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7205 chanserv_adjust_limit(void *data)
7207 struct mod_chanmode change;
7208 struct chanData *cData = data;
7209 struct chanNode *channel = cData->channel;
7212 if(IsSuspended(cData))
7215 cData->limitAdjusted = now;
7216 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7217 if(cData->modes.modes_set & MODE_LIMIT)
7219 if(limit > cData->modes.new_limit)
7220 limit = cData->modes.new_limit;
7221 else if(limit == cData->modes.new_limit)
7225 mod_chanmode_init(&change);
7226 change.modes_set = MODE_LIMIT;
7227 change.new_limit = limit;
7228 mod_chanmode_announce(chanserv, channel, &change);
7232 handle_new_channel(struct chanNode *channel)
7234 struct chanData *cData;
7236 if(!(cData = channel->channel_info))
7239 if(cData->modes.modes_set || cData->modes.modes_clear)
7240 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7242 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7243 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7246 void handle_new_channel_created(char *chan, struct userNode *user) {
7247 if(user->handle_info && chanserv_conf.new_channel_authed) {
7248 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7249 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7250 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7252 if(chanserv_conf.new_channel_msg)
7253 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7256 /* Welcome to my worst nightmare. Warning: Read (or modify)
7257 the code below at your own risk. */
7259 handle_join(struct modeNode *mNode)
7261 struct mod_chanmode change;
7262 struct userNode *user = mNode->user;
7263 struct chanNode *channel = mNode->channel;
7264 struct chanData *cData;
7265 struct userData *uData = NULL;
7266 struct banData *bData;
7267 struct handle_info *handle;
7268 unsigned int modes = 0, info = 0;
7272 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7275 cData = channel->channel_info;
7276 if(channel->members.used > cData->max) {
7277 cData->max = channel->members.used;
7278 cData->max_time = now;
7281 for(i = 0; i < channel->invited.used; i++)
7283 if(channel->invited.list[i] == user) {
7284 userList_remove(&channel->invited, user);
7288 /* Check for bans. If they're joining through a ban, one of two
7290 * 1: Join during a netburst, by riding the break. Kick them
7291 * unless they have ops or voice in the channel.
7292 * 2: They're allowed to join through the ban (an invite in
7293 * ircu2.10, or a +e on Hybrid, or something).
7294 * If they're not joining through a ban, and the banlist is not
7295 * full, see if they're on the banlist for the channel. If so,
7298 if(user->uplink->burst && !mNode->modes)
7301 for(ii = 0; ii < channel->banlist.used; ii++)
7303 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7305 /* Riding a netburst. Naughty. */
7306 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7312 mod_chanmode_init(&change);
7314 if(channel->banlist.used < MAXBANS)
7316 /* Not joining through a ban. */
7317 for(bData = cData->bans;
7318 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7319 bData = bData->next);
7323 char kick_reason[MAXLEN];
7324 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7326 bData->triggered = now;
7327 if(bData != cData->bans)
7329 /* Shuffle the ban to the head of the list. */
7331 bData->next->prev = bData->prev;
7333 bData->prev->next = bData->next;
7336 bData->next = cData->bans;
7339 cData->bans->prev = bData;
7340 cData->bans = bData;
7343 change.args[0].mode = MODE_BAN;
7344 change.args[0].u.hostmask = bData->mask;
7345 mod_chanmode_announce(chanserv, channel, &change);
7346 KickChannelUser(user, channel, chanserv, kick_reason);
7351 /* ChanServ will not modify the limits in join-flooded channels,
7352 or when there are enough slots left below the limit. */
7353 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7354 && !channel->join_flooded
7355 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7357 /* The user count has begun "bumping" into the channel limit,
7358 so set a timer to raise the limit a bit. Any previous
7359 timers are removed so three incoming users within the delay
7360 results in one limit change, not three. */
7362 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7363 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7366 if(channel->join_flooded)
7368 /* don't automatically give ops or voice during a join flood */
7370 else if(cData->lvlOpts[lvlGiveOps] == 0)
7371 modes |= MODE_CHANOP;
7372 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7373 modes |= MODE_VOICE;
7375 greeting = cData->greeting;
7376 if(user->handle_info)
7378 handle = user->handle_info;
7380 if(IsHelper(user) && !IsHelping(user))
7383 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7385 if(channel == chanserv_conf.support_channels.list[ii])
7387 HANDLE_SET_FLAG(user->handle_info, HELPING);
7393 uData = GetTrueChannelAccess(cData, handle);
7394 if(uData && !IsUserSuspended(uData))
7396 /* Ops and above were handled by the above case. */
7397 if(IsUserAutoOp(uData))
7399 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7400 modes |= MODE_CHANOP;
7401 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7402 modes |= MODE_VOICE;
7404 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7405 cData->visited = now;
7406 if(cData->user_greeting)
7407 greeting = cData->user_greeting;
7409 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7410 && ((now - uData->seen) >= chanserv_conf.info_delay)
7418 /* If user joining normally (not during burst), apply op or voice,
7419 * and send greeting/userinfo as appropriate.
7421 if(!user->uplink->burst)
7425 if(modes & MODE_CHANOP)
7426 modes &= ~MODE_VOICE;
7427 change.args[0].mode = modes;
7428 change.args[0].u.member = mNode;
7429 mod_chanmode_announce(chanserv, channel, &change);
7432 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7433 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7434 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7440 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7442 struct mod_chanmode change;
7443 struct userData *channel;
7444 unsigned int ii, jj;
7446 if(!user->handle_info)
7449 mod_chanmode_init(&change);
7451 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7453 struct chanNode *cn;
7454 struct modeNode *mn;
7455 if(IsUserSuspended(channel)
7456 || IsSuspended(channel->channel)
7457 || !(cn = channel->channel->channel))
7460 mn = GetUserMode(cn, user);
7463 if(!IsUserSuspended(channel)
7464 && IsUserAutoInvite(channel)
7465 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7467 && !user->uplink->burst)
7468 irc_invite(chanserv, user, cn);
7472 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7473 channel->channel->visited = now;
7475 if(IsUserAutoOp(channel))
7477 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7478 change.args[0].mode = MODE_CHANOP;
7479 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7480 change.args[0].mode = MODE_VOICE;
7482 change.args[0].mode = 0;
7483 change.args[0].u.member = mn;
7484 if(change.args[0].mode)
7485 mod_chanmode_announce(chanserv, cn, &change);
7488 channel->seen = now;
7489 channel->present = 1;
7492 for(ii = 0; ii < user->channels.used; ++ii)
7494 struct chanNode *chan = user->channels.list[ii]->channel;
7495 struct banData *ban;
7497 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7498 || !chan->channel_info
7499 || IsSuspended(chan->channel_info))
7501 for(jj = 0; jj < chan->banlist.used; ++jj)
7502 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7504 if(jj < chan->banlist.used)
7506 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7508 char kick_reason[MAXLEN];
7509 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7511 change.args[0].mode = MODE_BAN;
7512 change.args[0].u.hostmask = ban->mask;
7513 mod_chanmode_announce(chanserv, chan, &change);
7514 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7515 KickChannelUser(user, chan, chanserv, kick_reason);
7516 ban->triggered = now;
7521 if(IsSupportHelper(user))
7523 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7525 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7527 HANDLE_SET_FLAG(user->handle_info, HELPING);
7535 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7537 struct chanData *cData;
7538 struct userData *uData;
7540 cData = mn->channel->channel_info;
7541 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7544 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7546 /* Allow for a bit of padding so that the limit doesn't
7547 track the user count exactly, which could get annoying. */
7548 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7550 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7551 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7555 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7557 scan_user_presence(uData, mn->user);
7559 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7560 cData->visited = now;
7563 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7566 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7567 struct chanNode *channel;
7568 struct userNode *exclude;
7569 /* When looking at the channel that is being /part'ed, we
7570 * have to skip over the client that is leaving. For
7571 * other channels, we must not do that.
7573 channel = chanserv_conf.support_channels.list[ii];
7574 exclude = (channel == mn->channel) ? mn->user : NULL;
7575 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7578 if(ii == chanserv_conf.support_channels.used)
7579 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7584 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7586 struct userData *uData;
7588 if(!channel->channel_info || !kicker || IsService(kicker)
7589 || (kicker == victim) || IsSuspended(channel->channel_info)
7590 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7593 if(protect_user(victim, kicker, channel->channel_info))
7595 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7596 KickChannelUser(kicker, channel, chanserv, reason);
7599 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7604 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7606 struct chanData *cData;
7608 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7611 cData = channel->channel_info;
7612 if(bad_topic(channel, user, channel->topic))
7614 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7615 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7616 SetChannelTopic(channel, chanserv, old_topic, 1);
7617 else if(cData->topic)
7618 SetChannelTopic(channel, chanserv, cData->topic, 1);
7621 /* With topicsnarf, grab the topic and save it as the default topic. */
7622 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7625 cData->topic = strdup(channel->topic);
7631 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7633 struct mod_chanmode *bounce = NULL;
7634 unsigned int bnc, ii;
7637 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7640 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7641 && mode_lock_violated(&channel->channel_info->modes, change))
7643 char correct[MAXLEN];
7644 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7645 mod_chanmode_format(&channel->channel_info->modes, correct);
7646 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7648 for(ii = bnc = 0; ii < change->argc; ++ii)
7650 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7652 const struct userNode *victim = change->args[ii].u.member->user;
7653 if(!protect_user(victim, user, channel->channel_info))
7656 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7659 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7660 bounce->args[bnc].u.member = GetUserMode(channel, user);
7661 if(bounce->args[bnc].u.member)
7665 bounce->args[bnc].mode = MODE_CHANOP;
7666 bounce->args[bnc].u.member = change->args[ii].u.member;
7668 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7670 else if(change->args[ii].mode & MODE_CHANOP)
7672 const struct userNode *victim = change->args[ii].u.member->user;
7673 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7676 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7677 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7678 bounce->args[bnc].u.member = change->args[ii].u.member;
7681 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7683 const char *ban = change->args[ii].u.hostmask;
7684 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7687 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7688 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7689 bounce->args[bnc].u.hostmask = strdup(ban);
7691 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7696 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7697 mod_chanmode_announce(chanserv, channel, bounce);
7698 for(ii = 0; ii < change->argc; ++ii)
7699 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7700 free((char*)bounce->args[ii].u.hostmask);
7701 mod_chanmode_free(bounce);
7706 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7708 struct chanNode *channel;
7709 struct banData *bData;
7710 struct mod_chanmode change;
7711 unsigned int ii, jj;
7712 char kick_reason[MAXLEN];
7714 mod_chanmode_init(&change);
7716 change.args[0].mode = MODE_BAN;
7717 for(ii = 0; ii < user->channels.used; ++ii)
7719 channel = user->channels.list[ii]->channel;
7720 /* Need not check for bans if they're opped or voiced. */
7721 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7723 /* Need not check for bans unless channel registration is active. */
7724 if(!channel->channel_info || IsSuspended(channel->channel_info))
7726 /* Look for a matching ban already on the channel. */
7727 for(jj = 0; jj < channel->banlist.used; ++jj)
7728 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7730 /* Need not act if we found one. */
7731 if(jj < channel->banlist.used)
7733 /* Look for a matching ban in this channel. */
7734 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7736 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7738 change.args[0].u.hostmask = bData->mask;
7739 mod_chanmode_announce(chanserv, channel, &change);
7740 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7741 KickChannelUser(user, channel, chanserv, kick_reason);
7742 bData->triggered = now;
7743 break; /* we don't need to check any more bans in the channel */
7748 static void handle_rename(struct handle_info *handle, const char *old_handle)
7750 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7754 dict_remove2(handle_dnrs, old_handle, 1);
7755 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7756 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7761 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7763 struct userNode *h_user;
7765 if(handle->channels)
7767 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7768 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7770 while(handle->channels)
7771 del_channel_user(handle->channels, 1);
7776 handle_server_link(UNUSED_ARG(struct server *server))
7778 struct chanData *cData;
7780 for(cData = channelList; cData; cData = cData->next)
7782 if(!IsSuspended(cData))
7783 cData->may_opchan = 1;
7784 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7785 && !cData->channel->join_flooded
7786 && ((cData->channel->limit - cData->channel->members.used)
7787 < chanserv_conf.adjust_threshold))
7789 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7790 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7796 chanserv_conf_read(void)
7800 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7801 struct mod_chanmode *change;
7802 struct string_list *strlist;
7803 struct chanNode *chan;
7806 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7808 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7811 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7812 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7813 chanserv_conf.support_channels.used = 0;
7814 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7816 for(ii = 0; ii < strlist->used; ++ii)
7818 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7821 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7823 channelList_append(&chanserv_conf.support_channels, chan);
7826 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7829 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7832 chan = AddChannel(str, now, str2, NULL);
7834 channelList_append(&chanserv_conf.support_channels, chan);
7836 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7837 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7838 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7839 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7840 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7841 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7842 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7843 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7844 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7845 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7846 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7847 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7848 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7849 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7850 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7851 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7852 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7853 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7854 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7855 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7856 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7857 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7858 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7859 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7860 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7861 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7862 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7863 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7864 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7865 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7866 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7868 NickChange(chanserv, str, 0);
7869 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7870 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7871 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7872 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7873 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7874 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7875 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7876 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7877 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7878 chanserv_conf.max_owned = str ? atoi(str) : 5;
7879 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7880 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7881 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7882 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7883 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7884 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7885 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7886 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7887 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7888 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7889 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7890 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7891 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7894 safestrncpy(mode_line, str, sizeof(mode_line));
7895 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7896 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7897 && (change->argc < 2))
7899 chanserv_conf.default_modes = *change;
7900 mod_chanmode_free(change);
7902 free_string_list(chanserv_conf.set_shows);
7903 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7905 strlist = string_list_copy(strlist);
7908 static const char *list[] = {
7909 /* free form text */
7910 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7911 /* options based on user level */
7912 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7913 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7914 /* multiple choice options */
7915 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7916 /* binary options */
7917 "DynLimit", "NoDelete", "expire", "Vote",
7921 strlist = alloc_string_list(ArrayLength(list)-1);
7922 for(ii=0; list[ii]; ii++)
7923 string_list_append(strlist, strdup(list[ii]));
7925 chanserv_conf.set_shows = strlist;
7926 /* We don't look things up now, in case the list refers to options
7927 * defined by modules initialized after this point. Just mark the
7928 * function list as invalid, so it will be initialized.
7930 set_shows_list.used = 0;
7931 free_string_list(chanserv_conf.eightball);
7932 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7935 strlist = string_list_copy(strlist);
7939 strlist = alloc_string_list(4);
7940 string_list_append(strlist, strdup("Yes."));
7941 string_list_append(strlist, strdup("No."));
7942 string_list_append(strlist, strdup("Maybe so."));
7944 chanserv_conf.eightball = strlist;
7945 free_string_list(chanserv_conf.old_ban_names);
7946 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7948 strlist = string_list_copy(strlist);
7950 strlist = alloc_string_list(2);
7951 chanserv_conf.old_ban_names = strlist;
7952 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7953 off_channel = str ? atoi(str) : 0;
7957 chanserv_note_type_read(const char *key, struct record_data *rd)
7960 struct note_type *ntype;
7963 if(!(obj = GET_RECORD_OBJECT(rd)))
7965 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7968 if(!(ntype = chanserv_create_note_type(key)))
7970 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7974 /* Figure out set access */
7975 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7977 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7978 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7980 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7982 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7983 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7985 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7987 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7991 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7992 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7993 ntype->set_access.min_opserv = 0;
7996 /* Figure out visibility */
7997 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7998 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7999 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8000 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8001 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8002 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8003 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8004 ntype->visible_type = NOTE_VIS_ALL;
8006 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8008 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8009 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8013 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8015 struct vote_option *vOpt;
8018 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8020 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8024 vOpt = calloc(1, sizeof(*vOpt));
8025 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8026 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8027 vOpt->voted = str ? atoi(str) : 0;
8028 vOpt->option_id = str ? atoi(key) : 0;
8029 vOpt->option_str = strdup(key);
8030 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8034 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8036 struct handle_info *handle;
8037 struct userData *uData;
8038 char *seen, *inf, *flags, *voted, *votefor;
8039 unsigned long last_seen;
8040 unsigned short access_level;
8042 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8044 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8048 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8049 if(access_level > UL_OWNER)
8051 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8055 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8056 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8057 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8058 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8059 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8060 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8061 handle = get_handle_info(key);
8064 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8068 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8069 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8071 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8072 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8080 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8082 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8083 unsigned long set_time, triggered_time, expires_time;
8085 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8087 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8091 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8092 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8093 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8094 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8095 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8096 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8097 if (!reason || !owner)
8100 set_time = set ? strtoul(set, NULL, 0) : now;
8101 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8103 expires_time = strtoul(s_expires, NULL, 0);
8105 expires_time = set_time + atoi(s_duration);
8109 if(!reason || (expires_time && (expires_time < now)))
8112 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8115 static struct suspended *
8116 chanserv_read_suspended(dict_t obj)
8118 struct suspended *suspended = calloc(1, sizeof(*suspended));
8122 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8123 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8124 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8125 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8126 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8127 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8128 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8129 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8130 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8131 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8135 static struct giveownership *
8136 chanserv_read_giveownership(dict_t obj)
8138 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8142 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8143 giveownership->staff_issuer = str ? strdup(str) : NULL;
8145 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8147 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8148 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8150 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8151 giveownership->reason = str ? strdup(str) : NULL;
8152 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8153 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8155 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8156 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8157 return giveownership;
8161 chanserv_channel_read(const char *key, struct record_data *hir)
8163 struct suspended *suspended;
8164 struct giveownership *giveownership;
8165 struct mod_chanmode *modes;
8166 struct chanNode *cNode;
8167 struct chanData *cData;
8168 struct dict *channel, *obj;
8169 char *str, *argv[10];
8173 channel = hir->d.object;
8175 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8178 cNode = AddChannel(key, now, NULL, NULL);
8181 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8184 cData = register_channel(cNode, str);
8187 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8191 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8193 enum levelOption lvlOpt;
8194 enum charOption chOpt;
8196 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8197 cData->flags = atoi(str);
8199 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8201 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8203 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8204 else if(levelOptions[lvlOpt].old_flag)
8206 if(cData->flags & levelOptions[lvlOpt].old_flag)
8207 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8209 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8213 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8215 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8217 cData->chOpts[chOpt] = str[0];
8220 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8222 enum levelOption lvlOpt;
8223 enum charOption chOpt;
8226 cData->flags = base64toint(str, 5);
8227 count = strlen(str += 5);
8228 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8231 if(levelOptions[lvlOpt].old_flag)
8233 if(cData->flags & levelOptions[lvlOpt].old_flag)
8234 lvl = levelOptions[lvlOpt].flag_value;
8236 lvl = levelOptions[lvlOpt].default_value;
8238 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8240 case 'c': lvl = UL_COOWNER; break;
8241 case 'm': lvl = UL_MASTER; break;
8242 case 'n': lvl = UL_OWNER+1; break;
8243 case 'o': lvl = UL_OP; break;
8244 case 'p': lvl = UL_PEON; break;
8245 case 'w': lvl = UL_OWNER; break;
8246 default: lvl = 0; break;
8248 cData->lvlOpts[lvlOpt] = lvl;
8250 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8251 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8254 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8256 cData->expiry = atoi(str);
8257 if(cData->expiry > 0) {
8258 if(cData->expiry > now) {
8259 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8261 timeq_add(1, chanserv_expire_channel, cData);
8268 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8270 suspended = chanserv_read_suspended(obj);
8271 cData->suspended = suspended;
8272 suspended->cData = cData;
8273 /* We could use suspended->expires and suspended->revoked to
8274 * set the CHANNEL_SUSPENDED flag, but we don't. */
8276 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8278 suspended = calloc(1, sizeof(*suspended));
8279 suspended->issued = 0;
8280 suspended->revoked = 0;
8281 suspended->suspender = strdup(str);
8282 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8283 suspended->expires = str ? atoi(str) : 0;
8284 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8285 suspended->reason = strdup(str ? str : "No reason");
8286 suspended->previous = NULL;
8287 cData->suspended = suspended;
8288 suspended->cData = cData;
8292 cData->flags &= ~CHANNEL_SUSPENDED;
8293 suspended = NULL; /* to squelch a warning */
8296 if(IsSuspended(cData)) {
8297 if(suspended->expires > now)
8298 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8299 else if(suspended->expires)
8300 cData->flags &= ~CHANNEL_SUSPENDED;
8303 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8305 giveownership = chanserv_read_giveownership(obj);
8306 cData->giveownership = giveownership;
8309 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8310 struct mod_chanmode change;
8311 mod_chanmode_init(&change);
8313 change.args[0].mode = MODE_CHANOP;
8314 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8315 mod_chanmode_announce(chanserv, cNode, &change);
8318 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8319 cData->registered = str ? strtoul(str, NULL, 0) : now;
8320 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8321 cData->visited = str ? strtoul(str, NULL, 0) : now;
8322 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8323 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8324 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8325 cData->max = str ? atoi(str) : 0;
8326 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8327 cData->max_time = str ? atoi(str) : 0;
8328 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8329 cData->greeting = str ? strdup(str) : NULL;
8330 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8331 cData->user_greeting = str ? strdup(str) : NULL;
8332 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8333 cData->topic_mask = str ? strdup(str) : NULL;
8334 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8335 cData->topic = str ? strdup(str) : NULL;
8337 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8339 cData->vote = str ? strdup(str) : NULL;
8340 dict_delete(cData->vote_options);
8341 cData->vote_options = dict_new();
8342 dict_set_free_data(cData->vote_options, free_vote_options);
8343 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8344 cData->vote_start = str ? atoi(str) : 0;
8345 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8346 for(it = dict_first(obj); it; it = iter_next(it)) {
8347 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8351 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8352 for(it = dict_first(obj); it; it = iter_next(it))
8354 struct record_data *rd = iter_data(it);
8355 if(rd->type != RECDB_QSTRING) continue;
8356 int advtopic_index = atoi(iter_key(it));
8357 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8358 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8361 if(!IsSuspended(cData)
8362 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8363 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8364 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8365 cData->modes = *modes;
8367 cData->modes.modes_set |= MODE_REGISTERED;
8368 if(cData->modes.argc > 1)
8369 cData->modes.argc = 1;
8370 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8371 mod_chanmode_free(modes);
8374 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8375 for(it = dict_first(obj); it; it = iter_next(it))
8376 user_read_helper(iter_key(it), iter_data(it), cData);
8378 if(!cData->users && !IsProtected(cData))
8380 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8381 unregister_channel(cData, "has empty user list.");
8385 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8386 for(it = dict_first(obj); it; it = iter_next(it))
8387 ban_read_helper(iter_key(it), iter_data(it), cData);
8389 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8390 for(it = dict_first(obj); it; it = iter_next(it))
8392 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8393 struct record_data *rd = iter_data(it);
8394 const char *note, *setter;
8396 if(rd->type != RECDB_OBJECT)
8398 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8402 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8404 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8406 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8410 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8411 if(!setter) setter = "<unknown>";
8412 chanserv_add_channel_note(cData, ntype, setter, note);
8420 chanserv_dnr_read(const char *key, struct record_data *hir)
8422 const char *setter, *reason, *str;
8423 struct do_not_register *dnr;
8424 unsigned long expiry;
8426 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8429 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8432 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8435 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8438 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8439 expiry = str ? strtoul(str, NULL, 0) : 0;
8440 if(expiry && expiry <= now)
8442 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8445 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8447 dnr->set = atoi(str);
8453 chanserv_saxdb_read(struct dict *database)
8455 struct dict *section;
8458 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8459 for(it = dict_first(section); it; it = iter_next(it))
8460 chanserv_note_type_read(iter_key(it), iter_data(it));
8462 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8463 for(it = dict_first(section); it; it = iter_next(it))
8464 chanserv_channel_read(iter_key(it), iter_data(it));
8466 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8467 for(it = dict_first(section); it; it = iter_next(it))
8468 chanserv_dnr_read(iter_key(it), iter_data(it));
8474 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8476 int high_present = 0;
8477 saxdb_start_record(ctx, KEY_USERS, 1);
8478 for(; uData; uData = uData->next)
8480 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8482 saxdb_start_record(ctx, uData->handle->handle, 0);
8483 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8484 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8486 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8487 if(uData->channel->vote && uData->voted)
8488 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8489 if(uData->channel->vote && uData->votefor)
8490 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8492 saxdb_write_string(ctx, KEY_INFO, uData->info);
8493 saxdb_end_record(ctx);
8495 saxdb_end_record(ctx);
8496 return high_present;
8500 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8504 saxdb_start_record(ctx, KEY_BANS, 1);
8505 for(; bData; bData = bData->next)
8507 saxdb_start_record(ctx, bData->mask, 0);
8508 saxdb_write_int(ctx, KEY_SET, bData->set);
8509 if(bData->triggered)
8510 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8512 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8514 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8516 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8517 saxdb_end_record(ctx);
8519 saxdb_end_record(ctx);
8523 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8525 saxdb_start_record(ctx, name, 0);
8526 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8527 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8529 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8531 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8533 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8535 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8536 saxdb_end_record(ctx);
8540 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8542 saxdb_start_record(ctx, name, 0);
8543 if(giveownership->staff_issuer)
8544 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8545 if(giveownership->old_owner)
8546 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8547 if(giveownership->target)
8548 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8549 if(giveownership->target_access)
8550 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8551 if(giveownership->reason)
8552 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8553 if(giveownership->issued)
8554 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8555 if(giveownership->previous)
8556 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8557 saxdb_end_record(ctx);
8561 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8565 enum levelOption lvlOpt;
8566 enum charOption chOpt;
8569 saxdb_start_record(ctx, channel->channel->name, 1);
8571 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8572 saxdb_write_int(ctx, KEY_MAX, channel->max);
8573 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8575 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8576 if(channel->registrar)
8577 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8578 if(channel->greeting)
8579 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8580 if(channel->user_greeting)
8581 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8582 if(channel->topic_mask)
8583 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8584 if(channel->suspended)
8585 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8586 if(channel->giveownership)
8587 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8589 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8592 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8593 if(channel->vote_start)
8594 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8595 if (dict_size(channel->vote_options)) {
8596 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8597 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8598 struct vote_option *vOpt = iter_data(it);
8600 sprintf(str,"%i",vOpt->option_id);
8601 saxdb_start_record(ctx, str, 0);
8603 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8605 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8606 saxdb_end_record(ctx);
8608 saxdb_end_record(ctx);
8612 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8613 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8614 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8615 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8616 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8618 buf[0] = channel->chOpts[chOpt];
8620 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8622 saxdb_end_record(ctx);
8624 if(channel->modes.modes_set || channel->modes.modes_clear)
8626 mod_chanmode_format(&channel->modes, buf);
8627 saxdb_write_string(ctx, KEY_MODES, buf);
8630 high_present = chanserv_write_users(ctx, channel->users);
8631 chanserv_write_bans(ctx, channel->bans);
8633 if(channel->flags & CHANNEL_ADVTOPIC) {
8634 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8636 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8637 if(channel->advtopic[advtopic_index])
8638 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8640 saxdb_end_record(ctx);
8643 if(dict_size(channel->notes))
8647 saxdb_start_record(ctx, KEY_NOTES, 1);
8648 for(it = dict_first(channel->notes); it; it = iter_next(it))
8650 struct note *note = iter_data(it);
8651 saxdb_start_record(ctx, iter_key(it), 0);
8652 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8653 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8654 saxdb_end_record(ctx);
8656 saxdb_end_record(ctx);
8659 if(channel->ownerTransfer)
8660 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8661 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8662 saxdb_end_record(ctx);
8666 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8670 saxdb_start_record(ctx, ntype->name, 0);
8671 switch(ntype->set_access_type)
8673 case NOTE_SET_CHANNEL_ACCESS:
8674 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8676 case NOTE_SET_CHANNEL_SETTER:
8677 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8679 case NOTE_SET_PRIVILEGED: default:
8680 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8683 switch(ntype->visible_type)
8685 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8686 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8687 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8689 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8690 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8691 saxdb_end_record(ctx);
8695 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8697 struct do_not_register *dnr;
8698 dict_iterator_t it, next;
8700 for(it = dict_first(dnrs); it; it = next)
8702 next = iter_next(it);
8703 dnr = iter_data(it);
8704 if(dnr->expires && dnr->expires <= now)
8706 dict_remove(dnrs, iter_key(it));
8709 saxdb_start_record(ctx, dnr->chan_name, 0);
8711 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8713 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8714 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8715 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8716 saxdb_end_record(ctx);
8721 chanserv_saxdb_write(struct saxdb_context *ctx)
8724 struct chanData *channel;
8727 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8728 for(it = dict_first(note_types); it; it = iter_next(it))
8729 chanserv_write_note_type(ctx, iter_data(it));
8730 saxdb_end_record(ctx);
8733 saxdb_start_record(ctx, KEY_DNR, 1);
8734 write_dnrs_helper(ctx, handle_dnrs);
8735 write_dnrs_helper(ctx, plain_dnrs);
8736 write_dnrs_helper(ctx, mask_dnrs);
8737 saxdb_end_record(ctx);
8740 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8741 for(channel = channelList; channel; channel = channel->next)
8742 chanserv_write_channel(ctx, channel);
8743 saxdb_end_record(ctx);
8749 chanserv_db_cleanup(void) {
8751 unreg_part_func(handle_part);
8753 unregister_channel(channelList, "terminating.");
8754 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8755 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8756 free(chanserv_conf.support_channels.list);
8757 dict_delete(handle_dnrs);
8758 dict_delete(plain_dnrs);
8759 dict_delete(mask_dnrs);
8760 dict_delete(note_types);
8761 free_string_list(chanserv_conf.eightball);
8762 free_string_list(chanserv_conf.old_ban_names);
8763 free_string_list(chanserv_conf.set_shows);
8764 free(set_shows_list.list);
8765 free(uset_shows_list.list);
8768 struct userData *helper = helperList;
8769 helperList = helperList->next;
8774 #if defined(GCC_VARMACROS)
8775 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8776 #elif defined(C99_VARMACROS)
8777 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8779 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8780 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8783 init_chanserv(const char *nick)
8785 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8786 conf_register_reload(chanserv_conf_read);
8790 reg_server_link_func(handle_server_link);
8791 reg_new_channel_func(handle_new_channel);
8792 reg_join_func(handle_join);
8793 reg_part_func(handle_part);
8794 reg_kick_func(handle_kick);
8795 reg_topic_func(handle_topic);
8796 reg_mode_change_func(handle_mode);
8797 reg_nick_change_func(handle_nick_change);
8798 reg_auth_func(handle_auth);
8801 reg_handle_rename_func(handle_rename);
8802 reg_unreg_func(handle_unreg);
8804 handle_dnrs = dict_new();
8805 dict_set_free_data(handle_dnrs, free);
8806 plain_dnrs = dict_new();
8807 dict_set_free_data(plain_dnrs, free);
8808 mask_dnrs = dict_new();
8809 dict_set_free_data(mask_dnrs, free);
8811 reg_svccmd_unbind_func(handle_svccmd_unbind);
8812 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8813 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8814 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8815 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8816 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8817 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8818 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8819 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8820 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8821 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8822 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8823 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8824 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8826 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8827 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8829 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8830 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8831 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8832 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8833 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8835 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8836 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8837 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8838 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8839 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8841 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8842 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8843 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8844 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8846 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8847 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8848 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8849 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8850 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8851 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8852 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8853 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8855 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8856 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8857 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8858 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8859 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8860 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8861 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8862 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8863 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8864 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8865 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8866 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8867 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8868 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8869 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8871 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8872 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8873 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8874 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8875 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8877 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8878 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8880 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8881 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8882 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8883 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8884 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8885 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8886 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8887 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8888 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8889 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8890 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8892 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8893 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8895 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8896 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8897 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8898 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8900 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8901 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8902 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8903 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8904 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8906 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8907 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8908 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8909 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8910 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8911 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8913 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8914 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8915 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8916 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8917 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8918 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8919 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8920 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8922 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8924 /* Channel options */
8925 DEFINE_CHANNEL_OPTION(defaulttopic);
8926 DEFINE_CHANNEL_OPTION(topicmask);
8927 DEFINE_CHANNEL_OPTION(greeting);
8928 DEFINE_CHANNEL_OPTION(usergreeting);
8929 DEFINE_CHANNEL_OPTION(modes);
8930 DEFINE_CHANNEL_OPTION(enfops);
8931 DEFINE_CHANNEL_OPTION(giveops);
8932 DEFINE_CHANNEL_OPTION(protect);
8933 DEFINE_CHANNEL_OPTION(enfmodes);
8934 DEFINE_CHANNEL_OPTION(enftopic);
8935 DEFINE_CHANNEL_OPTION(pubcmd);
8936 DEFINE_CHANNEL_OPTION(givevoice);
8937 DEFINE_CHANNEL_OPTION(userinfo);
8938 DEFINE_CHANNEL_OPTION(dynlimit);
8939 DEFINE_CHANNEL_OPTION(topicsnarf);
8940 DEFINE_CHANNEL_OPTION(vote);
8941 DEFINE_CHANNEL_OPTION(nodelete);
8942 DEFINE_CHANNEL_OPTION(toys);
8943 DEFINE_CHANNEL_OPTION(setters);
8944 DEFINE_CHANNEL_OPTION(topicrefresh);
8945 DEFINE_CHANNEL_OPTION(ctcpusers);
8946 DEFINE_CHANNEL_OPTION(ctcpreaction);
8947 DEFINE_CHANNEL_OPTION(inviteme);
8948 DEFINE_CHANNEL_OPTION(advtopic);
8949 DEFINE_CHANNEL_OPTION(unreviewed);
8950 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8951 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8952 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8954 DEFINE_CHANNEL_OPTION(offchannel);
8955 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8957 /* Alias set topic to set defaulttopic for compatibility. */
8958 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8961 DEFINE_USER_OPTION(noautoop);
8962 DEFINE_USER_OPTION(autoinvite);
8963 DEFINE_USER_OPTION(info);
8965 /* Alias uset autovoice to uset autoop. */
8966 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8968 note_types = dict_new();
8969 dict_set_free_data(note_types, chanserv_deref_note_type);
8972 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8973 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8974 service_register(chanserv)->trigger = '!';
8975 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8977 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8979 if(chanserv_conf.channel_expire_frequency)
8980 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8982 if(chanserv_conf.dnr_expire_frequency)
8983 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8985 if(chanserv_conf.refresh_period)
8987 unsigned long next_refresh;
8988 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8989 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8992 reg_exit_func(chanserv_db_cleanup);
8993 message_register_table(msgtab);