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." },
159 { "CMSG_ALERT_REGISTERED" "%s registered to %s by %s." },
161 /* Do-not-register channels */
162 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
163 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
164 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
165 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
166 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
167 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
168 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
169 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
170 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
171 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
172 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
173 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
174 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
175 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
177 /* Channel unregistration */
178 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
179 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
180 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
181 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
182 { "CMSG_ALERT_UNREGISTERED" "%s %s" },
185 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
186 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
188 /* Channel merging */
189 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
190 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
191 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
192 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
193 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
195 /* Handle unregistration */
196 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
199 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
200 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
201 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
202 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
203 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
204 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
205 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
206 { "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." },
207 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
208 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
209 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
210 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
211 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
212 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
214 /* Removing yourself from a channel. */
215 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
216 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
217 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
219 /* User management */
220 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
221 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
222 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
223 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
224 { "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." },
225 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
226 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
227 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
229 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
230 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
231 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
232 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
233 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
234 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
235 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
238 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
239 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
240 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
241 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
242 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
243 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
244 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
245 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
246 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
247 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
248 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
249 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
250 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
251 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
252 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
253 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
255 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
257 /* Channel management */
258 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
259 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
260 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
262 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
263 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
264 { "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" },
265 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
266 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
267 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
268 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
269 { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
271 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
272 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
273 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
274 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
275 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
276 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
277 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
278 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
279 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
280 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
281 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
282 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
283 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
284 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
285 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
286 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
287 { "CSMSG_SET_ADVTOPIC", "$bAdvTopic $b %s" },
288 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
289 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
290 { "CSMSG_SET_MODES", "$bModes $b %s" },
291 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
292 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
293 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
294 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
295 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
296 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
297 { "CSMSG_SET_VOTE", "$bVote $b %d" },
298 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
299 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
300 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
301 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
302 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
303 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
304 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
305 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
306 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
307 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
308 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
309 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
310 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
311 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
312 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
313 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
314 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
315 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
316 { "CSMSG_USET_INFO", "$bInfo $b %s" },
318 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
319 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
320 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
321 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
322 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
323 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
324 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
325 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
326 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
327 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
328 { "CSMSG_PROTECT_NONE", "No users will be protected." },
329 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
330 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
331 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
332 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
333 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
334 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
335 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
336 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
337 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
338 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
339 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
340 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
342 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
343 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
344 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
345 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
346 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
347 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
348 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
349 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
351 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
352 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
353 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
355 /* Channel userlist */
356 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
357 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
358 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
359 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
360 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
362 /* Channel note list */
363 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
364 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
365 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
366 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
367 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
368 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
369 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
370 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
371 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
372 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
373 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
374 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
375 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
376 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
377 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
379 /* Channel [un]suspension */
380 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
381 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
382 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
383 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
384 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
385 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
386 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
388 /* Access information */
389 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
390 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
391 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
392 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
393 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
394 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
395 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
396 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
397 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
398 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
399 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
400 { "CSMSG_UC_H_TITLE", "network helper" },
401 { "CSMSG_LC_H_TITLE", "support helper" },
402 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
403 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
404 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
407 /* Seen information */
408 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
409 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
410 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
411 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
413 /* Names information */
414 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
415 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
417 /* Channel information */
418 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
419 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
420 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
421 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
422 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
423 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
424 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
425 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
426 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
427 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
428 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
429 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
430 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
435 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
436 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
437 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
438 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
439 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
441 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
442 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
443 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
444 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
446 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
447 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
448 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
449 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
450 { "CSMSG_PEEK_OPS", "$bOps:$b" },
451 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
453 /* Network information */
454 { "CSMSG_NETWORK_INFO", "Network Information:" },
455 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
456 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
457 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
458 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
459 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
460 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
461 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
462 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
465 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
466 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
467 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
469 /* Channel searches */
470 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
471 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
472 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
473 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
475 /* Channel configuration */
476 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
477 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
478 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
479 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
482 { "CSMSG_USER_OPTIONS", "User Options:" },
483 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
486 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
487 { "CSMSG_PING_RESPONSE", "Pong!" },
488 { "CSMSG_WUT_RESPONSE", "wut" },
489 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
490 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
491 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
492 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
493 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
494 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
495 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
498 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
499 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
500 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
501 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
502 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
503 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
504 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
505 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
506 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
507 { "CSMSG_VOTE_QUESTION", "Question: %s" },
508 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
509 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
510 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
511 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
512 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
513 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
514 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
515 { "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." },
516 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
517 { "CSMSG_VOTE_VOTED", "You have already voted." },
518 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
519 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
520 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
521 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
524 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
528 /* eject_user and unban_user flags */
529 #define ACTION_KICK 0x0001
530 #define ACTION_BAN 0x0002
531 #define ACTION_ADD_BAN 0x0004
532 #define ACTION_ADD_TIMED_BAN 0x0008
533 #define ACTION_UNBAN 0x0010
534 #define ACTION_DEL_BAN 0x0020
536 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
537 #define MODELEN 40 + KEYLEN
541 #define CSFUNC_ARGS user, channel, argc, argv, cmd
543 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
544 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
545 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
546 reply("MSG_MISSING_PARAMS", argv[0]); \
550 DECLARE_LIST(dnrList, struct do_not_register *);
551 DEFINE_LIST(dnrList, struct do_not_register *)
553 #define chanserv_notice(target, format...) send_message(target , chanserv , ## format)
554 #define chanserv_oper_message(format...) do { if(chanserv_conf.oper_channel) send_channel_message(chanserv_conf.oper_channel , chanserv , ## format); } while(0)
556 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
558 struct userNode *chanserv;
561 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
562 static struct log_type *CS_LOG;
566 struct channelList support_channels;
567 struct mod_chanmode default_modes;
569 unsigned long db_backup_frequency;
570 unsigned long channel_expire_frequency;
571 unsigned long dnr_expire_frequency;
573 unsigned long invited_timeout;
575 unsigned long info_delay;
576 unsigned long adjust_delay;
577 unsigned long channel_expire_delay;
578 unsigned int nodelete_level;
580 unsigned int adjust_threshold;
581 int join_flood_threshold;
583 unsigned int greeting_length;
584 unsigned int refresh_period;
585 unsigned int giveownership_period;
587 unsigned int max_owned;
588 unsigned int max_chan_users;
589 unsigned int max_chan_bans;
590 unsigned int min_time_bans;
591 unsigned int max_userinfo_length;
593 unsigned int revoke_mode_a;
595 struct string_list *set_shows;
596 struct string_list *eightball;
597 struct string_list *old_ban_names;
599 const char *ctcp_short_ban_duration;
600 const char *ctcp_long_ban_duration;
602 const char *irc_operator_epithet;
603 const char *network_helper_epithet;
604 const char *support_helper_epithet;
606 const char *new_channel_authed;
607 const char *new_channel_unauthed;
608 const char *new_channel_msg;
610 struct chanNode *oper_channel;
615 struct userNode *user;
616 struct userNode *bot;
617 struct chanNode *channel;
619 unsigned short lowest;
620 unsigned short highest;
621 struct userData **users;
622 struct helpfile_table table;
627 struct userNode *user;
628 struct chanNode *chan;
631 enum note_access_type
633 NOTE_SET_CHANNEL_ACCESS,
634 NOTE_SET_CHANNEL_SETTER,
638 enum note_visible_type
641 NOTE_VIS_CHANNEL_USERS,
647 enum note_access_type set_access_type;
649 unsigned int min_opserv;
650 unsigned short min_ulevel;
652 enum note_visible_type visible_type;
653 unsigned int max_length;
660 struct note_type *type;
661 char setter[NICKSERV_HANDLE_LEN+1];
665 static unsigned int registered_channels;
666 static unsigned int banCount;
668 static const struct {
671 unsigned short level;
674 { "peon", "Peon", UL_PEON, '+' },
675 { "op", "Op", UL_OP, '@' },
676 { "master", "Master", UL_MASTER, '%' },
677 { "coowner", "Coowner", UL_COOWNER, '*' },
678 { "owner", "Owner", UL_OWNER, '!' },
679 { "helper", "BUG:", UL_HELPER, 'X' }
682 static const struct {
685 unsigned short default_value;
686 unsigned int old_idx;
687 unsigned int old_flag;
688 unsigned short flag_value;
690 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
691 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
692 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
693 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
694 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
695 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
696 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
697 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
698 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
699 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
700 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
701 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
704 struct charOptionValues {
707 } protectValues[] = {
708 { 'a', "CSMSG_PROTECT_ALL" },
709 { 'e', "CSMSG_PROTECT_EQUAL" },
710 { 'l', "CSMSG_PROTECT_LOWER" },
711 { 'n', "CSMSG_PROTECT_NONE" }
713 { 'd', "CSMSG_TOYS_DISABLED" },
714 { 'n', "CSMSG_TOYS_PRIVATE" },
715 { 'p', "CSMSG_TOYS_PUBLIC" }
716 }, topicRefreshValues[] = {
717 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
718 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
719 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
720 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
721 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
722 }, ctcpReactionValues[] = {
723 { 'k', "CSMSG_CTCPREACTION_KICK" },
724 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
725 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
726 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
729 static const struct {
733 unsigned int old_idx;
735 struct charOptionValues *values;
737 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
738 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
739 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
740 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
743 struct userData *helperList;
744 struct chanData *channelList;
745 static struct module *chanserv_module;
746 static unsigned int userCount;
748 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
749 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
750 static void unregister_channel(struct chanData *channel, const char *reason);
753 user_level_from_name(const char *name, unsigned short clamp_level)
755 unsigned int level = 0, ii;
757 level = strtoul(name, NULL, 10);
758 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
759 if(!irccasecmp(name, accessLevels[ii].name))
760 level = accessLevels[ii].level;
761 if(level > clamp_level)
767 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
770 *minl = strtoul(arg, &sep, 10);
778 *maxl = strtoul(sep+1, &sep, 10);
786 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
788 struct userData *uData, **head;
790 if(!channel || !handle)
793 if(override && HANDLE_FLAGGED(handle, HELPING)
794 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
796 for(uData = helperList;
797 uData && uData->handle != handle;
798 uData = uData->next);
802 uData = calloc(1, sizeof(struct userData));
803 uData->handle = handle;
805 uData->access = UL_HELPER;
811 uData->next = helperList;
813 helperList->prev = uData;
821 for(uData = channel->users; uData; uData = uData->next)
822 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
825 head = &(channel->users);
828 if(uData && (uData != *head))
830 /* Shuffle the user to the head of whatever list he was in. */
832 uData->next->prev = uData->prev;
834 uData->prev->next = uData->next;
840 (**head).prev = uData;
847 /* Returns non-zero if user has at least the minimum access.
848 * exempt_owner is set when handling !set, so the owner can set things
851 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
853 struct userData *uData;
854 struct chanData *cData = channel->channel_info;
855 unsigned short minimum = cData->lvlOpts[opt];
858 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
861 if(minimum <= uData->access)
863 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
868 /* Scan for other users authenticated to the same handle
869 still in the channel. If so, keep them listed as present.
871 user is optional, if not null, it skips checking that userNode
872 (for the handle_part function) */
874 scan_user_presence(struct userData *uData, struct userNode *user)
878 if(IsSuspended(uData->channel)
879 || IsUserSuspended(uData)
880 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
892 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
894 unsigned int eflags, argc;
896 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
898 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
899 if(!channel->channel_info
900 || IsSuspended(channel->channel_info)
902 || !ircncasecmp(text, "ACTION ", 7))
904 /* Figure out the minimum level needed to CTCP the channel */
905 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
907 /* We need to enforce against them; do so. */
909 argv[0] = (char*)text;
910 argv[1] = user->nick;
912 if(GetUserMode(channel, user))
913 eflags |= ACTION_KICK;
914 switch(channel->channel_info->chOpts[chCTCPReaction]) {
915 default: case 'k': /* just do the kick */ break;
917 eflags |= ACTION_BAN;
920 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
921 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
924 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
925 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
928 argv[argc++] = bad_ctcp_reason;
929 eject_user(chanserv, channel, argc, argv, NULL, eflags);
933 chanserv_create_note_type(const char *name)
935 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
936 strcpy(ntype->name, name);
938 dict_insert(note_types, ntype->name, ntype);
943 free_vote_options(void *data)
945 struct vote_option *vOpt = data;
947 free(vOpt->option_str);
952 chanserv_deref_note_type(void *data)
954 struct note_type *ntype = data;
956 if(--ntype->refs > 0)
962 chanserv_flush_note_type(struct note_type *ntype)
964 struct chanData *cData;
965 for(cData = channelList; cData; cData = cData->next)
966 dict_remove(cData->notes, ntype->name);
970 chanserv_truncate_notes(struct note_type *ntype)
972 struct chanData *cData;
974 unsigned int size = sizeof(*note) + ntype->max_length;
976 for(cData = channelList; cData; cData = cData->next) {
977 note = dict_find(cData->notes, ntype->name, NULL);
980 if(strlen(note->note) <= ntype->max_length)
982 dict_remove2(cData->notes, ntype->name, 1);
983 note = realloc(note, size);
984 note->note[ntype->max_length] = 0;
985 dict_insert(cData->notes, ntype->name, note);
989 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
992 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
995 unsigned int len = strlen(text);
997 if(len > type->max_length) len = type->max_length;
998 note = calloc(1, sizeof(*note) + len);
1000 strncpy(note->setter, setter, sizeof(note->setter)-1);
1001 memcpy(note->note, text, len);
1002 note->note[len] = 0;
1003 dict_insert(channel->notes, type->name, note);
1009 chanserv_free_note(void *data)
1011 struct note *note = data;
1013 chanserv_deref_note_type(note->type);
1014 assert(note->type->refs > 0); /* must use delnote to remove the type */
1018 static MODCMD_FUNC(cmd_createnote) {
1019 struct note_type *ntype;
1020 unsigned int arg = 1, existed = 0, max_length;
1022 if((ntype = dict_find(note_types, argv[1], NULL)))
1025 ntype = chanserv_create_note_type(argv[arg]);
1026 if(!irccasecmp(argv[++arg], "privileged"))
1029 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1030 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1032 else if(!irccasecmp(argv[arg], "channel"))
1034 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1037 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1040 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1041 ntype->set_access.min_ulevel = ulvl;
1043 else if(!irccasecmp(argv[arg], "setter"))
1045 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1049 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1053 if(!irccasecmp(argv[++arg], "privileged"))
1054 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1055 else if(!irccasecmp(argv[arg], "channel_users"))
1056 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1057 else if(!irccasecmp(argv[arg], "all"))
1058 ntype->visible_type = NOTE_VIS_ALL;
1060 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1064 if((arg+1) >= argc) {
1065 reply("MSG_MISSING_PARAMS", argv[0]);
1068 max_length = strtoul(argv[++arg], NULL, 0);
1069 if(max_length < 20 || max_length > 450)
1071 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1074 if(existed && (max_length < ntype->max_length))
1076 ntype->max_length = max_length;
1077 chanserv_truncate_notes(ntype);
1079 ntype->max_length = max_length;
1082 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1084 reply("CSMSG_NOTE_CREATED", ntype->name);
1089 dict_remove(note_types, ntype->name);
1093 static MODCMD_FUNC(cmd_removenote) {
1094 struct note_type *ntype;
1097 ntype = dict_find(note_types, argv[1], NULL);
1098 force = (argc > 2) && !irccasecmp(argv[2], "force");
1101 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1108 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1111 chanserv_flush_note_type(ntype);
1113 dict_remove(note_types, argv[1]);
1114 reply("CSMSG_NOTE_DELETED", argv[1]);
1119 chanserv_expire_channel(void *data)
1121 struct chanData *channel = data;
1122 char reason[MAXLEN];
1123 sprintf(reason, "channel expired.");
1124 channel->expiry = 0;
1125 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1126 unregister_channel(channel, reason);
1129 static MODCMD_FUNC(chan_opt_expire)
1131 struct chanData *cData = channel->channel_info;
1132 unsigned long value = cData->expiry;
1136 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1138 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1141 unsigned long expiry,duration;
1143 /* The two directions can have different ACLs. */
1144 if(!strcmp(argv[1], "0"))
1146 else if((duration = ParseInterval(argv[1])))
1147 expiry = now + duration;
1150 reply("MSG_INVALID_DURATION", argv[1]);
1154 if (expiry != value)
1158 timeq_del(value, chanserv_expire_channel, cData, 0);
1161 cData->expiry = value;
1164 timeq_add(expiry, chanserv_expire_channel, cData);
1169 if(cData->expiry > now) {
1170 char expirestr[INTERVALLEN];
1171 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1173 reply("CSMSG_SET_EXPIRE_OFF");
1178 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1182 if(orig->modes_set & change->modes_clear)
1184 if(orig->modes_clear & change->modes_set)
1186 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1187 && strcmp(orig->new_key, change->new_key))
1189 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1190 && (orig->new_limit != change->new_limit))
1195 static char max_length_text[MAXLEN+1][16];
1197 static struct helpfile_expansion
1198 chanserv_expand_variable(const char *variable)
1200 struct helpfile_expansion exp;
1202 if(!irccasecmp(variable, "notes"))
1205 exp.type = HF_TABLE;
1206 exp.value.table.length = 1;
1207 exp.value.table.width = 3;
1208 exp.value.table.flags = 0;
1209 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1210 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1211 exp.value.table.contents[0][0] = "Note Type";
1212 exp.value.table.contents[0][1] = "Visibility";
1213 exp.value.table.contents[0][2] = "Max Length";
1214 for(it=dict_first(note_types); it; it=iter_next(it))
1216 struct note_type *ntype = iter_data(it);
1219 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1220 row = exp.value.table.length++;
1221 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1222 exp.value.table.contents[row][0] = ntype->name;
1223 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1224 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1226 if(!max_length_text[ntype->max_length][0])
1227 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1228 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1233 exp.type = HF_STRING;
1234 exp.value.str = NULL;
1238 static struct chanData*
1239 register_channel(struct chanNode *cNode, char *registrar)
1241 struct chanData *channel;
1242 enum levelOption lvlOpt;
1243 enum charOption chOpt;
1246 channel = calloc(1, sizeof(struct chanData));
1248 channel->notes = dict_new();
1249 dict_set_free_data(channel->notes, chanserv_free_note);
1251 channel->registrar = strdup(registrar);
1252 channel->registered = now;
1253 channel->visited = now;
1254 channel->limitAdjusted = now;
1255 channel->ownerTransfer = now;
1256 channel->flags = CHANNEL_DEFAULT_FLAGS;
1257 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1258 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1259 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1260 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1261 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1262 channel->advtopic[i] = NULL;
1264 channel->prev = NULL;
1265 channel->next = channelList;
1268 channelList->prev = channel;
1269 channelList = channel;
1270 registered_channels++;
1272 channel->channel = cNode;
1274 cNode->channel_info = channel;
1276 channel->vote = NULL;
1281 static struct userData*
1282 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1284 struct userData *ud;
1286 if(access_level > UL_OWNER)
1289 ud = calloc(1, sizeof(*ud));
1290 ud->channel = channel;
1291 ud->handle = handle;
1293 ud->access = access_level;
1294 ud->info = info ? strdup(info) : NULL;
1297 ud->next = channel->users;
1299 channel->users->prev = ud;
1300 channel->users = ud;
1302 channel->userCount++;
1306 ud->u_next = ud->handle->channels;
1308 ud->u_next->u_prev = ud;
1309 ud->handle->channels = ud;
1315 del_channel_user(struct userData *user, int do_gc)
1317 struct chanData *channel = user->channel;
1319 channel->userCount--;
1323 user->prev->next = user->next;
1325 channel->users = user->next;
1327 user->next->prev = user->prev;
1330 user->u_prev->u_next = user->u_next;
1332 user->handle->channels = user->u_next;
1334 user->u_next->u_prev = user->u_prev;
1338 if(do_gc && !channel->users && !IsProtected(channel)) {
1339 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1340 unregister_channel(channel, "lost all users.");
1344 static void expire_ban(void *data);
1347 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1350 unsigned int ii, l1, l2;
1355 bd = malloc(sizeof(struct banData));
1357 bd->channel = channel;
1359 bd->triggered = triggered;
1360 bd->expires = expires;
1362 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1364 extern const char *hidden_host_suffix;
1365 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1369 l2 = strlen(old_name);
1372 if(irccasecmp(mask + l1 - l2, old_name))
1374 new_mask = alloca(MAXLEN);
1375 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1378 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1380 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1381 bd->reason = strdup(reason);
1384 timeq_add(expires, expire_ban, bd);
1387 bd->next = channel->bans;
1389 channel->bans->prev = bd;
1391 channel->banCount++;
1398 del_channel_ban(struct banData *ban)
1400 ban->channel->banCount--;
1404 ban->prev->next = ban->next;
1406 ban->channel->bans = ban->next;
1409 ban->next->prev = ban->prev;
1412 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1421 expire_ban(void *data)
1423 struct banData *bd = data;
1424 if(!IsSuspended(bd->channel))
1426 struct banList bans;
1427 struct mod_chanmode change;
1429 bans = bd->channel->channel->banlist;
1430 mod_chanmode_init(&change);
1431 for(ii=0; ii<bans.used; ii++)
1433 if(!strcmp(bans.list[ii]->ban, bd->mask))
1436 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1437 change.args[0].u.hostmask = bd->mask;
1438 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1444 del_channel_ban(bd);
1447 static void chanserv_expire_suspension(void *data);
1450 unregister_channel(struct chanData *channel, const char *reason)
1452 struct mod_chanmode change;
1453 char msgbuf[MAXLEN];
1456 /* After channel unregistration, the following must be cleaned
1458 - Channel information.
1461 - Channel suspension data.
1462 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1468 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1470 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1471 mod_chanmode_init(&change);
1473 change.modes_clear |= MODE_REGISTERED;
1474 if(chanserv_conf.revoke_mode_a)
1475 change.modes_clear |= MODE_ACCESS;
1476 mod_chanmode_announce(chanserv, channel->channel, &change);
1479 while(channel->users)
1480 del_channel_user(channel->users, 0);
1482 while(channel->bans)
1483 del_channel_ban(channel->bans);
1485 free(channel->topic);
1486 free(channel->registrar);
1487 free(channel->greeting);
1488 free(channel->user_greeting);
1489 free(channel->topic_mask);
1491 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1492 if(channel->advtopic[i])
1493 free(channel->advtopic[i]);
1497 channel->prev->next = channel->next;
1499 channelList = channel->next;
1502 channel->next->prev = channel->prev;
1504 if(channel->suspended)
1506 struct chanNode *cNode = channel->channel;
1507 struct suspended *suspended, *next_suspended;
1509 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1511 next_suspended = suspended->previous;
1512 free(suspended->suspender);
1513 free(suspended->reason);
1514 if(suspended->expires)
1515 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1520 cNode->channel_info = NULL;
1523 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1524 channel->channel->channel_info = NULL;
1526 dict_delete(channel->notes);
1527 if(!IsSuspended(channel))
1528 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1529 chanserv_oper_message(CSMSGL_CHANNEL_UNREGISTERED, channel->channel->name, reason);
1530 UnlockChannel(channel->channel);
1532 registered_channels--;
1536 expire_channels(void *data)
1538 struct chanData *channel, *next;
1539 struct userData *user;
1540 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1542 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1543 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1545 for(channel = channelList; channel; channel = next)
1547 next = channel->next;
1549 /* See if the channel can be expired. */
1550 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1551 || IsProtected(channel))
1554 /* Make sure there are no high-ranking users still in the channel. */
1555 for(user=channel->users; user; user=user->next)
1556 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1561 /* Unregister the channel */
1562 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1563 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1564 unregister_channel(channel, "registration expired.");
1567 if(chanserv_conf.channel_expire_frequency && !data)
1568 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1572 expire_dnrs(UNUSED_ARG(void *data))
1574 dict_iterator_t it, next;
1575 struct do_not_register *dnr;
1577 for(it = dict_first(handle_dnrs); it; it = next)
1579 dnr = iter_data(it);
1580 next = iter_next(it);
1581 if(dnr->expires && dnr->expires <= now)
1582 dict_remove(handle_dnrs, dnr->chan_name + 1);
1584 for(it = dict_first(plain_dnrs); it; it = next)
1586 dnr = iter_data(it);
1587 next = iter_next(it);
1588 if(dnr->expires && dnr->expires <= now)
1589 dict_remove(plain_dnrs, dnr->chan_name + 1);
1591 for(it = dict_first(mask_dnrs); it; it = next)
1593 dnr = iter_data(it);
1594 next = iter_next(it);
1595 if(dnr->expires && dnr->expires <= now)
1596 dict_remove(mask_dnrs, dnr->chan_name + 1);
1599 if(chanserv_conf.dnr_expire_frequency)
1600 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1604 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1606 char protect = channel->chOpts[chProtect];
1607 struct userData *cs_victim, *cs_aggressor;
1609 /* Don't protect if no one is to be protected, someone is attacking
1610 himself, or if the aggressor is an IRC Operator. */
1611 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1614 /* Don't protect if the victim isn't authenticated (because they
1615 can't be a channel user), unless we are to protect non-users
1617 cs_victim = GetChannelAccess(channel, victim->handle_info);
1618 if(protect != 'a' && !cs_victim)
1621 /* Protect if the aggressor isn't a user because at this point,
1622 the aggressor can only be less than or equal to the victim. */
1623 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1627 /* If the aggressor was a user, then the victim can't be helped. */
1634 if(cs_victim->access > cs_aggressor->access)
1639 if(cs_victim->access >= cs_aggressor->access)
1648 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1650 struct chanData *cData = channel->channel_info;
1651 struct userData *cs_victim;
1653 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1654 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1655 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1657 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1665 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1667 if(IsService(victim))
1669 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1673 if(protect_user(victim, user, channel->channel_info))
1675 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1682 static struct do_not_register *
1683 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1685 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1686 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1687 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1688 strcpy(dnr->reason, reason);
1690 dnr->expires = expires;
1691 if(dnr->chan_name[0] == '*')
1692 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1693 else if(strpbrk(dnr->chan_name, "*?"))
1694 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1696 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1700 static struct dnrList
1701 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1703 struct dnrList list;
1704 dict_iterator_t it, next;
1705 struct do_not_register *dnr;
1707 dnrList_init(&list);
1709 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1711 if(dnr->expires && dnr->expires <= now)
1712 dict_remove(handle_dnrs, handle);
1713 else if(list.used < max)
1714 dnrList_append(&list, dnr);
1717 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1719 if(dnr->expires && dnr->expires <= now)
1720 dict_remove(plain_dnrs, chan_name);
1721 else if(list.used < max)
1722 dnrList_append(&list, dnr);
1727 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1729 next = iter_next(it);
1730 if(!match_ircglob(chan_name, iter_key(it)))
1732 dnr = iter_data(it);
1733 if(dnr->expires && dnr->expires <= now)
1734 dict_remove(mask_dnrs, iter_key(it));
1736 dnrList_append(&list, dnr);
1743 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1745 struct userNode *user;
1746 char buf1[INTERVALLEN];
1747 char buf2[INTERVALLEN];
1754 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1759 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1760 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1764 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1767 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1772 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1774 struct dnrList list;
1777 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1778 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1779 dnr_print_func(list.list[ii], user);
1781 reply("CSMSG_MORE_DNRS", list.used - ii);
1786 struct do_not_register *
1787 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1789 struct dnrList list;
1790 struct do_not_register *dnr;
1792 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1793 dnr = list.used ? list.list[0] : NULL;
1798 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1800 struct do_not_register *dnr;
1801 dict_iterator_t it, next;
1802 unsigned int matches = 0;
1804 for(it = dict_first(dict); it; it = next)
1806 dnr = iter_data(it);
1807 next = iter_next(it);
1808 if(dnr->expires && dnr->expires <= now)
1810 dict_remove(dict, iter_key(it));
1813 dnr_print_func(dnr, user);
1820 static CHANSERV_FUNC(cmd_noregister)
1824 unsigned long expiry, duration;
1825 unsigned int matches;
1829 reply("CSMSG_DNR_SEARCH_RESULTS");
1830 matches = send_dnrs(user, handle_dnrs);
1831 matches += send_dnrs(user, plain_dnrs);
1832 matches += send_dnrs(user, mask_dnrs);
1834 reply("MSG_MATCH_COUNT", matches);
1836 reply("MSG_NO_MATCHES");
1842 if(!IsChannelName(target) && (*target != '*'))
1844 reply("CSMSG_NOT_DNR", target);
1852 reply("MSG_INVALID_DURATION", argv[2]);
1856 if(!strcmp(argv[2], "0"))
1858 else if((duration = ParseInterval(argv[2])))
1859 expiry = now + duration;
1862 reply("MSG_INVALID_DURATION", argv[2]);
1866 reason = unsplit_string(argv + 3, argc - 3, NULL);
1867 if((*target == '*') && !get_handle_info(target + 1))
1869 reply("MSG_HANDLE_UNKNOWN", target + 1);
1872 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1873 reply("CSMSG_NOREGISTER_CHANNEL", target);
1877 reply("CSMSG_DNR_SEARCH_RESULTS");
1879 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1881 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1883 reply("MSG_NO_MATCHES");
1887 static CHANSERV_FUNC(cmd_allowregister)
1889 const char *chan_name = argv[1];
1891 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1892 || dict_remove(plain_dnrs, chan_name)
1893 || dict_remove(mask_dnrs, chan_name))
1895 reply("CSMSG_DNR_REMOVED", chan_name);
1898 reply("CSMSG_NO_SUCH_DNR", chan_name);
1903 struct userNode *source;
1907 unsigned long min_set, max_set;
1908 unsigned long min_expires, max_expires;
1913 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1915 return !((dnr->set < search->min_set)
1916 || (dnr->set > search->max_set)
1917 || (dnr->expires < search->min_expires)
1918 || (search->max_expires
1919 && ((dnr->expires == 0)
1920 || (dnr->expires > search->max_expires)))
1921 || (search->chan_mask
1922 && !match_ircglob(dnr->chan_name, search->chan_mask))
1923 || (search->setter_mask
1924 && !match_ircglob(dnr->setter, search->setter_mask))
1925 || (search->reason_mask
1926 && !match_ircglob(dnr->reason, search->reason_mask)));
1929 static struct dnr_search *
1930 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1932 struct dnr_search *discrim;
1935 discrim = calloc(1, sizeof(*discrim));
1936 discrim->source = user;
1937 discrim->chan_mask = NULL;
1938 discrim->setter_mask = NULL;
1939 discrim->reason_mask = NULL;
1940 discrim->max_set = INT_MAX;
1941 discrim->limit = 50;
1943 for(ii=0; ii<argc; ++ii)
1947 reply("MSG_MISSING_PARAMS", argv[ii]);
1950 else if(0 == irccasecmp(argv[ii], "channel"))
1952 discrim->chan_mask = argv[++ii];
1954 else if(0 == irccasecmp(argv[ii], "setter"))
1956 discrim->setter_mask = argv[++ii];
1958 else if(0 == irccasecmp(argv[ii], "reason"))
1960 discrim->reason_mask = argv[++ii];
1962 else if(0 == irccasecmp(argv[ii], "limit"))
1964 discrim->limit = strtoul(argv[++ii], NULL, 0);
1966 else if(0 == irccasecmp(argv[ii], "set"))
1968 const char *cmp = argv[++ii];
1971 discrim->min_set = now - ParseInterval(cmp + 2);
1973 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1974 } else if(cmp[0] == '=') {
1975 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1976 } else if(cmp[0] == '>') {
1978 discrim->max_set = now - ParseInterval(cmp + 2);
1980 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1982 discrim->max_set = now - (ParseInterval(cmp) - 1);
1985 else if(0 == irccasecmp(argv[ii], "expires"))
1987 const char *cmp = argv[++ii];
1990 discrim->max_expires = now + ParseInterval(cmp + 2);
1992 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1993 } else if(cmp[0] == '=') {
1994 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1995 } else if(cmp[0] == '>') {
1997 discrim->min_expires = now + ParseInterval(cmp + 2);
1999 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2001 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2006 reply("MSG_INVALID_CRITERIA", argv[ii]);
2017 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2020 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2022 struct do_not_register *dnr;
2023 dict_iterator_t next;
2028 /* Initialize local variables. */
2031 if(discrim->chan_mask)
2033 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2034 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2038 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2040 /* Check against account-based DNRs. */
2041 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2042 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2045 else if(target_fixed)
2047 /* Check against channel-based DNRs. */
2048 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2049 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2054 /* Exhaustively search account DNRs. */
2055 for(it = dict_first(handle_dnrs); it; it = next)
2057 next = iter_next(it);
2058 dnr = iter_data(it);
2059 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2063 /* Do the same for channel DNRs. */
2064 for(it = dict_first(plain_dnrs); it; it = next)
2066 next = iter_next(it);
2067 dnr = iter_data(it);
2068 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2072 /* Do the same for wildcarded channel DNRs. */
2073 for(it = dict_first(mask_dnrs); it; it = next)
2075 next = iter_next(it);
2076 dnr = iter_data(it);
2077 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2085 dnr_remove_func(struct do_not_register *match, void *extra)
2087 struct userNode *user;
2090 chan_name = alloca(strlen(match->chan_name) + 1);
2091 strcpy(chan_name, match->chan_name);
2093 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2094 || dict_remove(plain_dnrs, chan_name)
2095 || dict_remove(mask_dnrs, chan_name))
2097 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2103 dnr_count_func(struct do_not_register *match, void *extra)
2105 return 0; (void)match; (void)extra;
2108 static MODCMD_FUNC(cmd_dnrsearch)
2110 struct dnr_search *discrim;
2111 dnr_search_func action;
2112 struct svccmd *subcmd;
2113 unsigned int matches;
2116 sprintf(buf, "dnrsearch %s", argv[1]);
2117 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2120 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2123 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2125 if(!irccasecmp(argv[1], "print"))
2126 action = dnr_print_func;
2127 else if(!irccasecmp(argv[1], "remove"))
2128 action = dnr_remove_func;
2129 else if(!irccasecmp(argv[1], "count"))
2130 action = dnr_count_func;
2133 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2137 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2141 if(action == dnr_print_func)
2142 reply("CSMSG_DNR_SEARCH_RESULTS");
2143 matches = dnr_search(discrim, action, user);
2145 reply("MSG_MATCH_COUNT", matches);
2147 reply("MSG_NO_MATCHES");
2153 chanserv_get_owned_count(struct handle_info *hi)
2155 struct userData *cList;
2158 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2159 if(cList->access == UL_OWNER)
2164 static CHANSERV_FUNC(cmd_register)
2166 struct handle_info *handle;
2167 struct chanData *cData;
2168 struct modeNode *mn;
2169 char reason[MAXLEN];
2171 unsigned int new_channel, force=0;
2172 struct do_not_register *dnr;
2176 if(channel->channel_info)
2178 reply("CSMSG_ALREADY_REGGED", channel->name);
2182 if(channel->bad_channel)
2184 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2189 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2191 reply("CSMSG_MUST_BE_OPPED", channel->name);
2196 chan_name = channel->name;
2200 if((argc < 2) || !IsChannelName(argv[1]))
2202 reply("MSG_NOT_CHANNEL_NAME");
2206 if(opserv_bad_channel(argv[1]))
2208 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2213 chan_name = argv[1];
2216 if(argc >= (new_channel+2))
2218 if(!IsHelping(user))
2220 reply("CSMSG_PROXY_FORBIDDEN");
2224 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2226 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2227 dnr = chanserv_is_dnr(chan_name, handle);
2231 handle = user->handle_info;
2232 dnr = chanserv_is_dnr(chan_name, handle);
2236 if(!IsHelping(user))
2237 reply("CSMSG_DNR_CHANNEL", chan_name);
2239 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2243 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2245 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2250 channel = AddChannel(argv[1], now, NULL, NULL);
2252 cData = register_channel(channel, user->handle_info->handle);
2253 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2254 cData->modes = chanserv_conf.default_modes;
2256 cData->modes.modes_set |= MODE_REGISTERED;
2257 if (IsOffChannel(cData))
2259 mod_chanmode_announce(chanserv, channel, &cData->modes);
2263 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2264 change->args[change->argc].mode = MODE_CHANOP;
2265 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2267 mod_chanmode_announce(chanserv, channel, change);
2268 mod_chanmode_free(change);
2271 /* Initialize the channel's max user record. */
2272 cData->max = channel->members.used;
2273 cData->max_time = 0;
2275 if(handle != user->handle_info)
2276 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2278 reply("CSMSG_REG_SUCCESS", channel->name);
2280 chanserv_oper_message(CSMSG_ALERT_REGISTERED, channel->name, handle->handle, user->handle_info->handle);
2285 make_confirmation_string(struct userData *uData)
2287 static char strbuf[16];
2292 for(src = uData->handle->handle; *src; )
2293 accum = accum * 31 + toupper(*src++);
2295 for(src = uData->channel->channel->name; *src; )
2296 accum = accum * 31 + toupper(*src++);
2297 sprintf(strbuf, "%08x", accum);
2301 static CHANSERV_FUNC(cmd_unregister)
2304 char reason[MAXLEN];
2305 struct chanData *cData;
2306 struct userData *uData;
2308 cData = channel->channel_info;
2311 reply("CSMSG_NOT_REGISTERED", channel->name);
2315 uData = GetChannelUser(cData, user->handle_info);
2316 if(!uData || (uData->access < UL_OWNER))
2318 reply("CSMSG_NO_ACCESS");
2322 if(IsProtected(cData) && !IsOper(user))
2324 reply("CSMSG_UNREG_NODELETE", channel->name);
2328 if(!IsHelping(user))
2330 const char *confirm_string;
2331 if(IsSuspended(cData))
2333 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2336 confirm_string = make_confirmation_string(uData);
2337 if((argc < 2) || strcmp(argv[1], confirm_string))
2339 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2344 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2345 name = strdup(channel->name);
2346 unregister_channel(cData, reason);
2347 spamserv_cs_unregister(user, channel, manually, "unregistered");
2348 reply("CSMSG_UNREG_SUCCESS", name);
2354 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2356 extern struct userNode *spamserv;
2357 struct mod_chanmode *change;
2359 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2361 change = mod_chanmode_alloc(2);
2363 change->args[0].mode = MODE_CHANOP;
2364 change->args[0].u.member = AddChannelUser(chanserv, channel);
2365 change->args[1].mode = MODE_CHANOP;
2366 change->args[1].u.member = AddChannelUser(spamserv, channel);
2370 change = mod_chanmode_alloc(1);
2372 change->args[0].mode = MODE_CHANOP;
2373 change->args[0].u.member = AddChannelUser(chanserv, channel);
2376 mod_chanmode_announce(chanserv, channel, change);
2377 mod_chanmode_free(change);
2380 static CHANSERV_FUNC(cmd_move)
2382 struct mod_chanmode change;
2383 struct chanNode *target;
2384 struct modeNode *mn;
2385 struct userData *uData;
2386 char reason[MAXLEN];
2387 struct do_not_register *dnr;
2388 int chanserv_join = 0, spamserv_join;
2392 if(IsProtected(channel->channel_info))
2394 reply("CSMSG_MOVE_NODELETE", channel->name);
2398 if(!IsChannelName(argv[1]))
2400 reply("MSG_NOT_CHANNEL_NAME");
2404 if(opserv_bad_channel(argv[1]))
2406 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2410 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2412 for(uData = channel->channel_info->users; uData; uData = uData->next)
2414 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2416 if(!IsHelping(user))
2417 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2419 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2425 mod_chanmode_init(&change);
2426 if(!(target = GetChannel(argv[1])))
2428 target = AddChannel(argv[1], now, NULL, NULL);
2429 if(!IsSuspended(channel->channel_info))
2432 else if(target->channel_info)
2434 reply("CSMSG_ALREADY_REGGED", target->name);
2437 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2438 && !IsHelping(user))
2440 reply("CSMSG_MUST_BE_OPPED", target->name);
2443 else if(!IsSuspended(channel->channel_info))
2448 /* Clear MODE_REGISTERED from old channel, add it to new. */
2450 change.modes_clear = MODE_REGISTERED;
2451 mod_chanmode_announce(chanserv, channel, &change);
2452 change.modes_clear = 0;
2453 change.modes_set = MODE_REGISTERED;
2454 mod_chanmode_announce(chanserv, target, &change);
2457 /* Move the channel_info to the target channel; it
2458 shouldn't be necessary to clear timeq callbacks
2459 for the old channel. */
2460 target->channel_info = channel->channel_info;
2461 target->channel_info->channel = target;
2462 channel->channel_info = NULL;
2464 /* Check whether users are present in the new channel. */
2465 for(uData = target->channel_info->users; uData; uData = uData->next)
2466 scan_user_presence(uData, NULL);
2468 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2471 ss_cs_join_channel(target, spamserv_join);
2473 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2474 if(!IsSuspended(target->channel_info))
2476 char reason2[MAXLEN];
2477 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2478 DelChannelUser(chanserv, channel, reason2, 0);
2480 UnlockChannel(channel);
2481 LockChannel(target);
2482 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2483 reply("CSMSG_MOVE_SUCCESS", target->name);
2488 merge_users(struct chanData *source, struct chanData *target)
2490 struct userData *suData, *tuData, *next;
2496 /* Insert the source's users into the scratch area. */
2497 for(suData = source->users; suData; suData = suData->next)
2498 dict_insert(merge, suData->handle->handle, suData);
2500 /* Iterate through the target's users, looking for
2501 users common to both channels. The lower access is
2502 removed from either the scratch area or target user
2504 for(tuData = target->users; tuData; tuData = next)
2506 struct userData *choice;
2508 next = tuData->next;
2510 /* If a source user exists with the same handle as a target
2511 channel's user, resolve the conflict by removing one. */
2512 suData = dict_find(merge, tuData->handle->handle, NULL);
2516 /* Pick the data we want to keep. */
2517 /* If the access is the same, use the later seen time. */
2518 if(suData->access == tuData->access)
2519 choice = (suData->seen > tuData->seen) ? suData : tuData;
2520 else /* Otherwise, keep the higher access level. */
2521 choice = (suData->access > tuData->access) ? suData : tuData;
2522 /* Use the later seen time. */
2523 if(suData->seen < tuData->seen)
2524 suData->seen = tuData->seen;
2526 tuData->seen = suData->seen;
2528 /* Remove the user that wasn't picked. */
2529 if(choice == tuData)
2531 dict_remove(merge, suData->handle->handle);
2532 del_channel_user(suData, 0);
2535 del_channel_user(tuData, 0);
2538 /* Move the remaining users to the target channel. */
2539 for(it = dict_first(merge); it; it = iter_next(it))
2541 suData = iter_data(it);
2543 /* Insert the user into the target channel's linked list. */
2544 suData->prev = NULL;
2545 suData->next = target->users;
2546 suData->channel = target;
2549 target->users->prev = suData;
2550 target->users = suData;
2552 /* Update the user counts for the target channel; the
2553 source counts are left alone. */
2554 target->userCount++;
2556 /* Check whether the user is in the target channel. */
2557 scan_user_presence(suData, NULL);
2560 /* Possible to assert (source->users == NULL) here. */
2561 source->users = NULL;
2566 merge_bans(struct chanData *source, struct chanData *target)
2568 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2570 /* Hold on to the original head of the target ban list
2571 to avoid comparing source bans with source bans. */
2572 tFront = target->bans;
2574 /* Perform a totally expensive O(n*m) merge, ick. */
2575 for(sbData = source->bans; sbData; sbData = sNext)
2577 /* Flag to track whether the ban's been moved
2578 to the destination yet. */
2581 /* Possible to assert (sbData->prev == NULL) here. */
2582 sNext = sbData->next;
2584 for(tbData = tFront; tbData; tbData = tNext)
2586 tNext = tbData->next;
2588 /* Perform two comparisons between each source
2589 and target ban, conflicts are resolved by
2590 keeping the broader ban and copying the later
2591 expiration and triggered time. */
2592 if(match_ircglobs(tbData->mask, sbData->mask))
2594 /* There is a broader ban in the target channel that
2595 overrides one in the source channel; remove the
2596 source ban and break. */
2597 if(sbData->expires > tbData->expires)
2598 tbData->expires = sbData->expires;
2599 if(sbData->triggered > tbData->triggered)
2600 tbData->triggered = sbData->triggered;
2601 del_channel_ban(sbData);
2604 else if(match_ircglobs(sbData->mask, tbData->mask))
2606 /* There is a broader ban in the source channel that
2607 overrides one in the target channel; remove the
2608 target ban, fall through and move the source over. */
2609 if(tbData->expires > sbData->expires)
2610 sbData->expires = tbData->expires;
2611 if(tbData->triggered > sbData->triggered)
2612 sbData->triggered = tbData->triggered;
2613 if(tbData == tFront)
2615 del_channel_ban(tbData);
2618 /* Source bans can override multiple target bans, so
2619 we allow a source to run through this loop multiple
2620 times, but we can only move it once. */
2625 /* Remove the source ban from the source ban list. */
2627 sbData->next->prev = sbData->prev;
2629 /* Modify the source ban's associated channel. */
2630 sbData->channel = target;
2632 /* Insert the ban into the target channel's linked list. */
2633 sbData->prev = NULL;
2634 sbData->next = target->bans;
2637 target->bans->prev = sbData;
2638 target->bans = sbData;
2640 /* Update the user counts for the target channel. */
2645 /* Possible to assert (source->bans == NULL) here. */
2646 source->bans = NULL;
2650 merge_data(struct chanData *source, struct chanData *target)
2652 /* Use more recent visited and owner-transfer time; use older
2653 * registered time. Bitwise or may_opchan. Use higher max.
2654 * Do not touch last_refresh, ban count or user counts.
2656 if(source->visited > target->visited)
2657 target->visited = source->visited;
2658 if(source->registered < target->registered)
2659 target->registered = source->registered;
2660 if(source->ownerTransfer > target->ownerTransfer)
2661 target->ownerTransfer = source->ownerTransfer;
2662 if(source->may_opchan)
2663 target->may_opchan = 1;
2664 if(source->max > target->max) {
2665 target->max = source->max;
2666 target->max_time = source->max_time;
2671 merge_channel(struct chanData *source, struct chanData *target)
2673 merge_users(source, target);
2674 merge_bans(source, target);
2675 merge_data(source, target);
2678 static CHANSERV_FUNC(cmd_merge)
2680 struct userData *target_user;
2681 struct chanNode *target;
2682 char reason[MAXLEN];
2686 /* Make sure the target channel exists and is registered to the user
2687 performing the command. */
2688 if(!(target = GetChannel(argv[1])))
2690 reply("MSG_INVALID_CHANNEL");
2694 if(!target->channel_info)
2696 reply("CSMSG_NOT_REGISTERED", target->name);
2700 if(IsProtected(channel->channel_info))
2702 reply("CSMSG_MERGE_NODELETE");
2706 if(IsSuspended(target->channel_info))
2708 reply("CSMSG_MERGE_SUSPENDED");
2712 if(channel == target)
2714 reply("CSMSG_MERGE_SELF");
2718 target_user = GetChannelUser(target->channel_info, user->handle_info);
2719 if(!target_user || (target_user->access < UL_OWNER))
2721 reply("CSMSG_MERGE_NOT_OWNER");
2725 /* Merge the channel structures and associated data. */
2726 merge_channel(channel->channel_info, target->channel_info);
2727 spamserv_cs_move_merge(user, channel, target, 0);
2728 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2729 unregister_channel(channel->channel_info, reason);
2730 reply("CSMSG_MERGE_SUCCESS", target->name);
2734 static CHANSERV_FUNC(cmd_opchan)
2736 struct mod_chanmode change;
2737 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2739 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2742 channel->channel_info->may_opchan = 0;
2743 mod_chanmode_init(&change);
2745 change.args[0].mode = MODE_CHANOP;
2746 change.args[0].u.member = GetUserMode(channel, chanserv);
2747 if(!change.args[0].u.member)
2749 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2752 mod_chanmode_announce(chanserv, channel, &change);
2753 reply("CSMSG_OPCHAN_DONE", channel->name);
2757 static CHANSERV_FUNC(cmd_adduser)
2759 struct userData *actee;
2760 struct userData *actor, *real_actor;
2761 struct handle_info *handle;
2762 unsigned short access_level, override = 0;
2766 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2768 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2772 access_level = user_level_from_name(argv[2], UL_OWNER);
2775 reply("CSMSG_INVALID_ACCESS", argv[2]);
2779 actor = GetChannelUser(channel->channel_info, user->handle_info);
2780 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2782 if(actor->access <= access_level)
2784 reply("CSMSG_NO_BUMP_ACCESS");
2788 /* Trying to add someone with equal/more access? */
2789 if (!real_actor || real_actor->access <= access_level)
2790 override = CMD_LOG_OVERRIDE;
2792 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2795 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2797 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2801 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2802 scan_user_presence(actee, NULL);
2803 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2804 return 1 | override;
2807 static CHANSERV_FUNC(cmd_clvl)
2809 struct handle_info *handle;
2810 struct userData *victim;
2811 struct userData *actor, *real_actor;
2812 unsigned short new_access, override = 0;
2813 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2817 actor = GetChannelUser(channel->channel_info, user->handle_info);
2818 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2820 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2823 if(handle == user->handle_info && !privileged)
2825 reply("CSMSG_NO_SELF_CLVL");
2829 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2831 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2835 if(actor->access <= victim->access && !privileged)
2837 reply("MSG_USER_OUTRANKED", handle->handle);
2841 new_access = user_level_from_name(argv[2], UL_OWNER);
2845 reply("CSMSG_INVALID_ACCESS", argv[2]);
2849 if(new_access >= actor->access && !privileged)
2851 reply("CSMSG_NO_BUMP_ACCESS");
2855 /* Trying to clvl a equal/higher user? */
2856 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2857 override = CMD_LOG_OVERRIDE;
2858 /* Trying to clvl someone to equal/higher access? */
2859 if(!real_actor || new_access >= real_actor->access)
2860 override = CMD_LOG_OVERRIDE;
2861 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2862 * If they lower their own access it's not a big problem.
2865 victim->access = new_access;
2866 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2867 return 1 | override;
2870 static CHANSERV_FUNC(cmd_deluser)
2872 struct handle_info *handle;
2873 struct userData *victim;
2874 struct userData *actor, *real_actor;
2875 unsigned short access_level, override = 0;
2880 actor = GetChannelUser(channel->channel_info, user->handle_info);
2881 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2883 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2886 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2888 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2894 access_level = user_level_from_name(argv[1], UL_OWNER);
2897 reply("CSMSG_INVALID_ACCESS", argv[1]);
2900 if(access_level != victim->access)
2902 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2908 access_level = victim->access;
2911 if((actor->access <= victim->access) && !IsHelping(user))
2913 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2917 /* If people delete themselves it is an override, but they
2918 * could've used deleteme so we don't log it as an override
2920 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2921 override = CMD_LOG_OVERRIDE;
2923 chan_name = strdup(channel->name);
2924 del_channel_user(victim, 1);
2925 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2927 return 1 | override;
2931 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2933 struct userData *actor, *real_actor, *uData, *next;
2934 unsigned int override = 0;
2936 actor = GetChannelUser(channel->channel_info, user->handle_info);
2937 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2939 if(min_access > max_access)
2941 reply("CSMSG_BAD_RANGE", min_access, max_access);
2945 if(actor->access <= max_access)
2947 reply("CSMSG_NO_ACCESS");
2951 if(!real_actor || real_actor->access <= max_access)
2952 override = CMD_LOG_OVERRIDE;
2954 for(uData = channel->channel_info->users; uData; uData = next)
2958 if((uData->access >= min_access)
2959 && (uData->access <= max_access)
2960 && match_ircglob(uData->handle->handle, mask))
2961 del_channel_user(uData, 1);
2964 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2965 return 1 | override;
2968 static CHANSERV_FUNC(cmd_mdelowner)
2970 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2973 static CHANSERV_FUNC(cmd_mdelcoowner)
2975 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2978 static CHANSERV_FUNC(cmd_mdelmaster)
2980 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2983 static CHANSERV_FUNC(cmd_mdelop)
2985 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2988 static CHANSERV_FUNC(cmd_mdelpeon)
2990 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2994 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2996 struct banData *bData, *next;
2997 char interval[INTERVALLEN];
2999 unsigned long limit;
3002 limit = now - duration;
3003 for(bData = channel->channel_info->bans; bData; bData = next)
3007 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3010 del_channel_ban(bData);
3014 intervalString(interval, duration, user->handle_info);
3015 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3020 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3022 struct userData *actor, *uData, *next;
3023 char interval[INTERVALLEN];
3025 unsigned long limit;
3027 actor = GetChannelUser(channel->channel_info, user->handle_info);
3028 if(min_access > max_access)
3030 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3034 if(!actor || actor->access <= max_access)
3036 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3041 limit = now - duration;
3042 for(uData = channel->channel_info->users; uData; uData = next)
3046 if((uData->seen > limit)
3048 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3051 if(((uData->access >= min_access) && (uData->access <= max_access))
3052 || (!max_access && (uData->access < actor->access)))
3054 del_channel_user(uData, 1);
3062 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3064 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3068 static CHANSERV_FUNC(cmd_trim)
3070 unsigned long duration;
3071 unsigned short min_level, max_level;
3076 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3077 duration = ParseInterval(argv[2]);
3080 reply("CSMSG_CANNOT_TRIM");
3084 if(!irccasecmp(argv[1], "bans"))
3086 cmd_trim_bans(user, channel, duration);
3089 else if(!irccasecmp(argv[1], "users"))
3091 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3094 else if(parse_level_range(&min_level, &max_level, argv[1]))
3096 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3099 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3101 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3106 reply("CSMSG_INVALID_TRIM", argv[1]);
3111 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3112 to the user. cmd_all takes advantage of this. */
3113 static CHANSERV_FUNC(cmd_up)
3115 struct mod_chanmode change;
3116 struct userData *uData;
3119 mod_chanmode_init(&change);
3121 change.args[0].u.member = GetUserMode(channel, user);
3122 if(!change.args[0].u.member)
3125 reply("MSG_CHANNEL_ABSENT", channel->name);
3129 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3133 reply("CSMSG_GODMODE_UP", argv[0]);
3136 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3138 change.args[0].mode = MODE_CHANOP;
3139 errmsg = "CSMSG_ALREADY_OPPED";
3141 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3143 change.args[0].mode = MODE_VOICE;
3144 errmsg = "CSMSG_ALREADY_VOICED";
3149 reply("CSMSG_NO_ACCESS");
3152 change.args[0].mode &= ~change.args[0].u.member->modes;
3153 if(!change.args[0].mode)
3156 reply(errmsg, channel->name);
3159 modcmd_chanmode_announce(&change);
3163 static CHANSERV_FUNC(cmd_down)
3165 struct mod_chanmode change;
3167 mod_chanmode_init(&change);
3169 change.args[0].u.member = GetUserMode(channel, user);
3170 if(!change.args[0].u.member)
3173 reply("MSG_CHANNEL_ABSENT", channel->name);
3177 if(!change.args[0].u.member->modes)
3180 reply("CSMSG_ALREADY_DOWN", channel->name);
3184 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3185 modcmd_chanmode_announce(&change);
3189 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)
3191 struct userData *cList;
3193 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3195 if(IsSuspended(cList->channel)
3196 || IsUserSuspended(cList)
3197 || !GetUserMode(cList->channel->channel, user))
3200 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3206 static CHANSERV_FUNC(cmd_upall)
3208 return cmd_all(CSFUNC_ARGS, cmd_up);
3211 static CHANSERV_FUNC(cmd_downall)
3213 return cmd_all(CSFUNC_ARGS, cmd_down);
3216 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3217 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3220 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)
3222 unsigned int ii, valid;
3223 struct userNode *victim;
3224 struct mod_chanmode *change;
3226 change = mod_chanmode_alloc(argc - 1);
3228 for(ii=valid=0; ++ii < argc; )
3230 if(!(victim = GetUserH(argv[ii])))
3232 change->args[valid].mode = mode;
3233 change->args[valid].u.member = GetUserMode(channel, victim);
3234 if(!change->args[valid].u.member)
3236 if(validate && !validate(user, channel, victim))
3241 change->argc = valid;
3242 if(valid < (argc-1))
3243 reply("CSMSG_PROCESS_FAILED");
3246 modcmd_chanmode_announce(change);
3247 reply(action, channel->name);
3249 mod_chanmode_free(change);
3253 static CHANSERV_FUNC(cmd_op)
3255 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3258 static CHANSERV_FUNC(cmd_deop)
3260 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3263 static CHANSERV_FUNC(cmd_voice)
3265 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3268 static CHANSERV_FUNC(cmd_devoice)
3270 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3273 static CHANSERV_FUNC(cmd_opme)
3275 struct mod_chanmode change;
3278 mod_chanmode_init(&change);
3280 change.args[0].u.member = GetUserMode(channel, user);
3281 if(!change.args[0].u.member)
3284 reply("MSG_CHANNEL_ABSENT", channel->name);
3288 struct devnull_class *devnull;
3289 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3291 change.args[0].mode = MODE_CHANOP;
3292 errmsg = "CSMSG_ALREADY_OPPED";
3297 reply("CSMSG_NO_ACCESS");
3300 change.args[0].mode &= ~change.args[0].u.member->modes;
3301 if(!change.args[0].mode)
3304 reply(errmsg, channel->name);
3307 modcmd_chanmode_announce(&change);
3312 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3318 for(ii=0; ii<channel->members.used; ii++)
3320 struct modeNode *mn = channel->members.list[ii];
3322 if(IsService(mn->user))
3325 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3328 if(protect_user(mn->user, user, channel->channel_info))
3332 victims[(*victimCount)++] = mn;
3338 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3340 struct userNode *victim;
3341 struct modeNode **victims;
3342 unsigned int offset, n, victimCount, duration = 0;
3343 char *reason = "Bye.", *ban, *name;
3344 char interval[INTERVALLEN];
3346 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3347 REQUIRE_PARAMS(offset);
3348 if(argc > offset && IsNetServ(user))
3350 if(*argv[offset] == '$') {
3351 struct userNode *hib;
3352 const char *accountnameb = argv[offset] + 1;
3353 if(!(hib = GetUserH(accountnameb)))
3355 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3364 reason = unsplit_string(argv + offset, argc - offset, NULL);
3365 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3367 /* Truncate the reason to a length of TOPICLEN, as
3368 the ircd does; however, leave room for an ellipsis
3369 and the kicker's nick. */
3370 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3374 if((victim = GetUserH(argv[1])))
3376 victims = alloca(sizeof(victims[0]));
3377 victims[0] = GetUserMode(channel, victim);
3378 /* XXX: The comparison with ACTION_KICK is just because all
3379 * other actions can work on users outside the channel, and we
3380 * want to allow those (e.g. unbans) in that case. If we add
3381 * some other ejection action for in-channel users, change
3383 victimCount = victims[0] ? 1 : 0;
3385 if(IsService(victim))
3387 reply("MSG_SERVICE_IMMUNE", victim->nick);
3391 if((action == ACTION_KICK) && !victimCount)
3393 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3397 if(protect_user(victim, user, channel->channel_info))
3399 reply("CSMSG_USER_PROTECTED", victim->nick);
3403 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3404 name = victim->nick;
3406 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3408 struct handle_info *hi;
3409 extern const char *titlehost_suffix;
3410 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3411 const char *accountname = argv[1] + 1;
3413 if(!(hi = get_handle_info(accountname)))
3415 reply("MSG_HANDLE_UNKNOWN", accountname);
3419 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3420 victims = alloca(sizeof(victims[0]) * channel->members.used);
3422 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3424 reply("CSMSG_MASK_PROTECTED", banmask);
3428 if((action == ACTION_KICK) && (victimCount == 0))
3430 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3434 name = ban = strdup(banmask);
3438 if(!is_ircmask(argv[1]))
3440 reply("MSG_NICK_UNKNOWN", argv[1]);
3444 victims = alloca(sizeof(victims[0]) * channel->members.used);
3446 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3448 reply("CSMSG_MASK_PROTECTED", argv[1]);
3452 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3454 reply("CSMSG_LAME_MASK", argv[1]);
3458 if((action == ACTION_KICK) && (victimCount == 0))
3460 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3464 name = ban = strdup(argv[1]);
3467 /* Truncate the ban in place if necessary; we must ensure
3468 that 'ban' is a valid ban mask before sanitizing it. */
3469 sanitize_ircmask(ban);
3471 if(action & ACTION_ADD_BAN)
3473 struct banData *bData, *next;
3475 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3477 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3482 if(action & ACTION_ADD_TIMED_BAN)
3484 duration = ParseInterval(argv[2]);
3486 if(duration < chanserv_conf.min_time_bans)
3488 reply("CSMSG_DURATION_TOO_LOW");
3492 else if(duration > (86400 * 365 * 2))
3494 reply("CSMSG_DURATION_TOO_HIGH");
3500 for(bData = channel->channel_info->bans; bData; bData = next)
3502 if(match_ircglobs(bData->mask, ban))
3504 int exact = !irccasecmp(bData->mask, ban);
3506 /* The ban is redundant; there is already a ban
3507 with the same effect in place. */
3511 free(bData->reason);
3512 bData->reason = strdup(reason);
3513 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3515 reply("CSMSG_REASON_CHANGE", ban);
3519 if(exact && bData->expires)
3523 /* If the ban matches an existing one exactly,
3524 extend the expiration time if the provided
3525 duration is longer. */
3526 if(duration && (now + duration > bData->expires))
3528 bData->expires = now + duration;
3539 /* Delete the expiration timeq entry and
3540 requeue if necessary. */
3541 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3544 timeq_add(bData->expires, expire_ban, bData);
3548 /* automated kickban */
3551 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3553 reply("CSMSG_BAN_ADDED", name, channel->name);
3559 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3566 if(match_ircglobs(ban, bData->mask))
3568 /* The ban we are adding makes previously existing
3569 bans redundant; silently remove them. */
3570 del_channel_ban(bData);
3574 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);
3576 name = ban = strdup(bData->mask);
3580 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3582 extern const char *hidden_host_suffix;
3583 const char *old_name = chanserv_conf.old_ban_names->list[n];
3585 unsigned int l1, l2;
3588 l2 = strlen(old_name);
3591 if(irccasecmp(ban + l1 - l2, old_name))
3593 new_mask = malloc(MAXLEN);
3594 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3596 name = ban = new_mask;
3601 if(action & ACTION_BAN)
3603 unsigned int exists;
3604 struct mod_chanmode *change;
3606 if(channel->banlist.used >= MAXBANS)
3609 reply("CSMSG_BANLIST_FULL", channel->name);
3614 exists = ChannelBanExists(channel, ban);
3615 change = mod_chanmode_alloc(victimCount + 1);
3616 for(n = 0; n < victimCount; ++n)
3618 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3619 change->args[n].u.member = victims[n];
3623 change->args[n].mode = MODE_BAN;
3624 change->args[n++].u.hostmask = ban;
3628 modcmd_chanmode_announce(change);
3630 mod_chanmode_announce(chanserv, channel, change);
3631 mod_chanmode_free(change);
3633 if(exists && (action == ACTION_BAN))
3636 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3642 if(action & ACTION_KICK)
3644 char kick_reason[MAXLEN];
3645 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3647 for(n = 0; n < victimCount; n++)
3648 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3653 /* No response, since it was automated. */
3655 else if(action & ACTION_ADD_BAN)
3658 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3660 reply("CSMSG_BAN_ADDED", name, channel->name);
3662 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3663 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3664 else if(action & ACTION_BAN)
3665 reply("CSMSG_BAN_DONE", name, channel->name);
3666 else if(action & ACTION_KICK && victimCount)
3667 reply("CSMSG_KICK_DONE", name, channel->name);
3673 static CHANSERV_FUNC(cmd_kickban)
3675 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3678 static CHANSERV_FUNC(cmd_kick)
3680 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3683 static CHANSERV_FUNC(cmd_ban)
3685 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3688 static CHANSERV_FUNC(cmd_addban)
3690 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3693 static CHANSERV_FUNC(cmd_addtimedban)
3695 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3698 struct mod_chanmode *
3699 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3701 struct mod_chanmode *change;
3702 unsigned char *match;
3703 unsigned int ii, count;
3705 match = alloca(bans->used);
3708 for(ii = count = 0; ii < bans->used; ++ii)
3710 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3711 MATCH_USENICK | MATCH_VISIBLE);
3718 for(ii = count = 0; ii < bans->used; ++ii)
3720 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3727 change = mod_chanmode_alloc(count);
3728 for(ii = count = 0; ii < bans->used; ++ii)
3732 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3733 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3735 assert(count == change->argc);
3740 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3742 struct userNode *actee;
3748 /* may want to allow a comma delimited list of users... */
3749 if(!(actee = GetUserH(argv[1])))
3751 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3753 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3754 const char *accountname = argv[1] + 1;
3756 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3757 mask = strdup(banmask);
3759 else if(!is_ircmask(argv[1]))
3761 reply("MSG_NICK_UNKNOWN", argv[1]);
3766 mask = strdup(argv[1]);
3770 /* We don't sanitize the mask here because ircu
3772 if(action & ACTION_UNBAN)
3774 struct mod_chanmode *change;
3775 change = find_matching_bans(&channel->banlist, actee, mask);
3780 modcmd_chanmode_announce(change);
3781 for(ii = 0; ii < change->argc; ++ii)
3782 free((char*)change->args[ii].u.hostmask);
3783 mod_chanmode_free(change);
3788 if(action & ACTION_DEL_BAN)
3790 struct banData *ban, *next;
3792 ban = channel->channel_info->bans;
3796 for( ; ban && !user_matches_glob(actee, ban->mask,
3797 MATCH_USENICK | MATCH_VISIBLE);
3800 for( ; ban && !match_ircglobs(mask, ban->mask);
3805 del_channel_ban(ban);
3812 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3814 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3820 static CHANSERV_FUNC(cmd_unban)
3822 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3825 static CHANSERV_FUNC(cmd_delban)
3827 /* it doesn't necessarily have to remove the channel ban - may want
3828 to make that an option. */
3829 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3832 static CHANSERV_FUNC(cmd_unbanme)
3834 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3835 long flags = ACTION_UNBAN;
3837 /* remove permanent bans if the user has the proper access. */
3838 if(uData->access >= UL_MASTER)
3839 flags |= ACTION_DEL_BAN;
3841 argv[1] = user->nick;
3842 return unban_user(user, channel, 2, argv, cmd, flags);
3845 static CHANSERV_FUNC(cmd_unbanall)
3847 struct mod_chanmode *change;
3850 if(!channel->banlist.used)
3852 reply("CSMSG_NO_BANS", channel->name);
3856 change = mod_chanmode_alloc(channel->banlist.used);
3857 for(ii=0; ii<channel->banlist.used; ii++)
3859 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3860 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3862 modcmd_chanmode_announce(change);
3863 for(ii = 0; ii < change->argc; ++ii)
3864 free((char*)change->args[ii].u.hostmask);
3865 mod_chanmode_free(change);
3866 reply("CSMSG_BANS_REMOVED", channel->name);
3870 static CHANSERV_FUNC(cmd_open)
3872 struct mod_chanmode *change;
3875 change = find_matching_bans(&channel->banlist, user, NULL);
3877 change = mod_chanmode_alloc(0);
3878 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3879 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3880 && channel->channel_info->modes.modes_set)
3881 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3882 modcmd_chanmode_announce(change);
3883 reply("CSMSG_CHANNEL_OPENED", channel->name);
3884 for(ii = 0; ii < change->argc; ++ii)
3885 free((char*)change->args[ii].u.hostmask);
3886 mod_chanmode_free(change);
3890 static CHANSERV_FUNC(cmd_myaccess)
3892 static struct string_buffer sbuf;
3893 struct handle_info *target_handle;
3894 struct userData *uData;
3899 target_handle = user->handle_info;
3900 else if(!IsStaff(user))
3902 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3905 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3908 if(!oper_outranks(user, target_handle))
3911 if(!target_handle->channels)
3913 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3917 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3918 for(uData = target_handle->channels; uData; uData = uData->u_next)
3920 struct chanData *cData = uData->channel;
3922 unsigned int base_len;
3924 if(uData->access > UL_OWNER)
3926 if(uData->access == UL_OWNER)
3929 if(IsProtected(cData)
3930 && (target_handle != user->handle_info)
3931 && !GetTrueChannelAccess(cData, user->handle_info)
3932 && !IsNetworkHelper(user))
3935 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3936 base_len = sbuf.used;
3937 if(IsUserSuspended(uData))
3938 string_buffer_append(&sbuf, 's');
3939 if(IsUserAutoOp(uData))
3941 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3942 string_buffer_append(&sbuf, 'o');
3943 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3944 string_buffer_append(&sbuf, 'v');
3946 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3947 string_buffer_append(&sbuf, 'i');
3948 if(sbuf.used==base_len)
3951 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3953 string_buffer_append_string(&sbuf, ")]");
3954 string_buffer_append(&sbuf, '\0');
3955 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3959 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3961 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3967 static CHANSERV_FUNC(cmd_access)
3969 struct userNode *target;
3970 struct handle_info *target_handle;
3971 struct userData *uData;
3973 char prefix[MAXLEN];
3978 target_handle = target->handle_info;
3980 else if((target = GetUserH(argv[1])))
3982 target_handle = target->handle_info;
3984 else if(argv[1][0] == '*')
3986 if(!(target_handle = get_handle_info(argv[1]+1)))
3988 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3994 reply("MSG_NICK_UNKNOWN", argv[1]);
3998 assert(target || target_handle);
4000 if(target == chanserv)
4002 reply("CSMSG_IS_CHANSERV");
4010 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4015 reply("MSG_USER_AUTHENTICATE", target->nick);
4018 reply("MSG_AUTHENTICATE");
4024 const char *epithet = NULL, *type = NULL;
4027 epithet = chanserv_conf.irc_operator_epithet;
4028 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4030 else if(IsNetworkHelper(target))
4032 epithet = chanserv_conf.network_helper_epithet;
4033 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4035 else if(IsSupportHelper(target))
4037 epithet = chanserv_conf.support_helper_epithet;
4038 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4042 if(target_handle->epithet)
4043 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4045 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4047 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4051 sprintf(prefix, "%s", target_handle->handle);
4054 if(!channel->channel_info)
4056 reply("CSMSG_NOT_REGISTERED", channel->name);
4060 helping = HANDLE_FLAGGED(target_handle, HELPING)
4061 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4062 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4064 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4065 /* To prevent possible information leaks, only show infolines
4066 * if the requestor is in the channel or it's their own
4068 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4070 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4072 /* Likewise, only say it's suspended if the user has active
4073 * access in that channel or it's their own entry. */
4074 if(IsUserSuspended(uData)
4075 && (GetChannelUser(channel->channel_info, user->handle_info)
4076 || (user->handle_info == uData->handle)))
4078 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4083 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4090 def_list(struct listData *list)
4094 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4096 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4097 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4098 if(list->table.length == 1)
4100 msg = user_find_message(list->user, "MSG_NONE");
4101 send_message_type(4, list->user, list->bot, " %s", msg);
4106 userData_access_comp(const void *arg_a, const void *arg_b)
4108 const struct userData *a = *(struct userData**)arg_a;
4109 const struct userData *b = *(struct userData**)arg_b;
4111 if(a->access != b->access)
4112 res = b->access - a->access;
4114 res = irccasecmp(a->handle->handle, b->handle->handle);
4119 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4121 void (*send_list)(struct listData *);
4122 struct userData *uData;
4123 struct listData lData;
4124 unsigned int matches;
4128 lData.bot = cmd->parent->bot;
4129 lData.channel = channel;
4130 lData.lowest = lowest;
4131 lData.highest = highest;
4132 lData.search = (argc > 1) ? argv[1] : NULL;
4133 send_list = def_list;
4135 if(user->handle_info)
4137 switch(user->handle_info->userlist_style)
4139 case HI_STYLE_DEF: send_list = def_list; break;
4140 case HI_STYLE_ZOOT: send_list = def_list; break;
4144 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4146 for(uData = channel->channel_info->users; uData; uData = uData->next)
4148 if((uData->access < lowest)
4149 || (uData->access > highest)
4150 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4152 lData.users[matches++] = uData;
4154 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4156 lData.table.length = matches+1;
4157 lData.table.width = 4;
4158 lData.table.flags = TABLE_NO_FREE;
4159 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4160 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4161 lData.table.contents[0] = ary;
4164 ary[2] = "Last Seen";
4166 for(matches = 1; matches < lData.table.length; ++matches)
4168 char seen[INTERVALLEN];
4170 uData = lData.users[matches-1];
4171 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4172 lData.table.contents[matches] = ary;
4173 ary[0] = strtab(uData->access);
4174 ary[1] = uData->handle->handle;
4177 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4179 else if(!uData->seen)
4182 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4183 ary[2] = strdup(ary[2]);
4184 if(IsUserSuspended(uData))
4185 ary[3] = "Suspended";
4186 else if(HANDLE_FLAGGED(uData->handle, OPER))
4187 ary[3] = "Operator";
4188 else if(HANDLE_FLAGGED(uData->handle, HELPING))
4190 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4192 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4193 ary[3] = "Vacation";
4194 else if(HANDLE_FLAGGED(uData->handle, BOT))
4200 for(matches = 1; matches < lData.table.length; ++matches)
4202 free((char*)lData.table.contents[matches][2]);
4203 free(lData.table.contents[matches]);
4205 free(lData.table.contents[0]);
4206 free(lData.table.contents);
4207 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4211 static CHANSERV_FUNC(cmd_users)
4213 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4216 static CHANSERV_FUNC(cmd_wlist)
4218 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4221 static CHANSERV_FUNC(cmd_clist)
4223 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4226 static CHANSERV_FUNC(cmd_mlist)
4228 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4231 static CHANSERV_FUNC(cmd_olist)
4233 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4236 static CHANSERV_FUNC(cmd_plist)
4238 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4241 static CHANSERV_FUNC(cmd_bans)
4243 struct userNode *search_u = NULL;
4244 struct helpfile_table tbl;
4245 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4246 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4247 const char *msg_never, *triggered, *expires;
4248 struct banData *ban, **bans;
4252 else if(strchr(search = argv[1], '!'))
4255 search_wilds = search[strcspn(search, "?*")];
4257 else if(!(search_u = GetUserH(search)))
4258 reply("MSG_NICK_UNKNOWN", search);
4260 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4262 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4266 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4271 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4274 bans[matches++] = ban;
4279 tbl.length = matches + 1;
4280 tbl.width = 4 + timed;
4282 tbl.flags = TABLE_NO_FREE;
4283 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4284 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4285 tbl.contents[0][0] = "Mask";
4286 tbl.contents[0][1] = "Set By";
4287 tbl.contents[0][2] = "Triggered";
4290 tbl.contents[0][3] = "Expires";
4291 tbl.contents[0][4] = "Reason";
4294 tbl.contents[0][3] = "Reason";
4297 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4299 free(tbl.contents[0]);
4304 msg_never = user_find_message(user, "MSG_NEVER");
4305 for(ii = 0; ii < matches; )
4311 else if(ban->expires)
4312 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4314 expires = msg_never;
4317 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4319 triggered = msg_never;
4321 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4322 tbl.contents[ii][0] = ban->mask;
4323 tbl.contents[ii][1] = ban->owner;
4324 tbl.contents[ii][2] = strdup(triggered);
4327 tbl.contents[ii][3] = strdup(expires);
4328 tbl.contents[ii][4] = ban->reason;
4331 tbl.contents[ii][3] = ban->reason;
4333 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4334 reply("MSG_MATCH_COUNT", matches);
4335 for(ii = 1; ii < tbl.length; ++ii)
4337 free((char*)tbl.contents[ii][2]);
4339 free((char*)tbl.contents[ii][3]);
4340 free(tbl.contents[ii]);
4342 free(tbl.contents[0]);
4348 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4350 struct chanData *cData = channel->channel_info;
4351 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4353 if(cData->topic_mask)
4355 if(cData->flags & CHANNEL_ADVTOPIC)
4357 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4358 char topicmask[TOPICLEN];
4359 int skipnum, topicpos = 0;
4360 char *ptr = cData->topic_mask;
4361 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4364 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4366 topicmask[topicpos++] = '*';
4368 topicmask[topicpos++] = *ptr;
4371 topicmask[topicpos++] = *ptr;
4375 topicmask[topicpos] = 0;
4376 return !match_ircglob(new_topic, topicmask);
4379 return !match_ircglob(new_topic, cData->topic_mask);
4381 else if(cData->topic)
4382 return irccasecmp(new_topic, cData->topic);
4387 static CHANSERV_FUNC(cmd_topic)
4389 struct chanData *cData;
4392 cData = channel->channel_info;
4397 SetChannelTopic(channel, chanserv, cData->topic, 1);
4398 reply("CSMSG_TOPIC_SET", cData->topic);
4402 reply("CSMSG_NO_TOPIC", channel->name);
4406 topic = unsplit_string(argv + 1, argc - 1, NULL);
4407 /* If they say "!topic *", use an empty topic. */
4408 if((topic[0] == '*') && (topic[1] == 0))
4410 if(bad_topic(channel, user, topic))
4412 char *topic_mask = cData->topic_mask;
4415 char new_topic[TOPICLEN+1], tchar;
4416 int pos=0, starpos=-1, dpos=0, len;
4418 if(cData->flags & CHANNEL_ADVTOPIC)
4420 //first check if there is a leading 'modifier id'
4421 int advtopic_index = 0;
4424 for(; topic[pos]; pos++)
4426 if(topic[pos] == ' ')
4428 //leading number found, cut off and store value in advtopic_index
4430 advtopic_index = atoi(topic) - 1; //no zerobase
4431 topic = &topic[pos+1];
4432 /* If they say "!topic 2 *", unset advtopic id 2. */
4433 if((topic[0] == '*') && (topic[1] == 0))
4436 if(!isdigit(topic[pos]))
4439 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4442 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4445 if(cData->advtopic[advtopic_index])
4446 free(cData->advtopic[advtopic_index]);
4447 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4448 char *ptr = topic_mask;
4449 while(*ptr && (dpos <= TOPICLEN))
4455 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4456 numbuf[numpos++] = *ptr;
4459 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4461 new_topic[dpos++] = *ptr; //is % again
4464 advtopic_index--; //no zero base
4465 if(!cData->advtopic[advtopic_index])
4466 break; //just leave it empty
4467 len = strlen(cData->advtopic[advtopic_index]);
4468 if((dpos + len) > TOPICLEN)
4469 len = TOPICLEN + 1 - dpos;
4470 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4474 ptr++; /* and fall through */
4477 new_topic[dpos++] = *ptr;
4483 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4490 len = strlen(topic);
4491 if((dpos + len) > TOPICLEN)
4492 len = TOPICLEN + 1 - dpos;
4493 memcpy(new_topic+dpos, topic, len);
4497 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4498 default: new_topic[dpos++] = tchar; break;
4501 if((dpos > TOPICLEN) || tchar)
4504 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4505 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4509 new_topic[dpos] = 0;
4510 SetChannelTopic(channel, chanserv, new_topic, 1);
4512 reply("CSMSG_TOPIC_LOCKED", channel->name);
4517 SetChannelTopic(channel, chanserv, topic, 1);
4519 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4521 /* Grab the topic and save it as the default topic. */
4523 cData->topic = strdup(channel->topic);
4529 static CHANSERV_FUNC(cmd_mode)
4531 struct userData *uData;
4532 struct mod_chanmode *change;
4538 change = &channel->channel_info->modes;
4539 if(change->modes_set || change->modes_clear) {
4540 modcmd_chanmode_announce(change);
4541 reply("CSMSG_DEFAULTED_MODES", channel->name);
4543 reply("CSMSG_NO_MODES", channel->name);
4547 uData = GetChannelUser(channel->channel_info, user->handle_info);
4549 base_oplevel = MAXOPLEVEL;
4550 else if (uData->access >= UL_OWNER)
4553 base_oplevel = 1 + UL_OWNER - uData->access;
4554 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4557 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4561 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4562 && mode_lock_violated(&channel->channel_info->modes, change))
4565 mod_chanmode_format(&channel->channel_info->modes, modes);
4566 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4570 modcmd_chanmode_announce(change);
4571 mod_chanmode_format(change, fmt);
4572 mod_chanmode_free(change);
4573 reply("CSMSG_MODES_SET", fmt);
4578 chanserv_del_invite_mark(void *data)
4580 struct ChanUser *chanuser = data;
4581 struct chanNode *channel = chanuser->chan;
4583 if(!channel) return;
4584 for(i = 0; i < channel->invited.used; i++)
4586 if(channel->invited.list[i] == chanuser->user) {
4587 userList_remove(&channel->invited, chanuser->user);
4593 static CHANSERV_FUNC(cmd_invite)
4595 struct userNode *invite;
4596 struct ChanUser *chanuser;
4601 if(!(invite = GetUserH(argv[1])))
4603 reply("MSG_NICK_UNKNOWN", argv[1]);
4610 if(GetUserMode(channel, invite))
4612 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4616 for(i = 0; i < channel->invited.used; i++)
4618 if(channel->invited.list[i] == invite) {
4619 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4628 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4629 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4632 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4634 irc_invite(chanserv, invite, channel);
4636 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4638 userList_append(&channel->invited, invite);
4639 chanuser = calloc(1, sizeof(*chanuser));
4640 chanuser->user=invite;
4641 chanuser->chan=channel;
4642 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4647 static CHANSERV_FUNC(cmd_inviteme)
4649 if(GetUserMode(channel, user))
4651 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4654 if(channel->channel_info
4655 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4657 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4660 irc_invite(cmd->parent->bot, user, channel);
4664 static CHANSERV_FUNC(cmd_invitemeall)
4666 struct handle_info *target = user->handle_info;
4667 struct userData *uData;
4669 if(!target->channels)
4671 reply("CSMSG_SQUAT_ACCESS", target->handle);
4675 for(uData = target->channels; uData; uData = uData->u_next)
4677 struct chanData *cData = uData->channel;
4678 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4680 irc_invite(cmd->parent->bot, user, cData->channel);
4687 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4690 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4692 /* We display things based on two dimensions:
4693 * - Issue time: present or absent
4694 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4695 * (in order of precedence, so something both expired and revoked
4696 * only counts as revoked)
4698 combo = (suspended->issued ? 4 : 0)
4699 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4701 case 0: /* no issue time, indefinite expiration */
4702 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4704 case 1: /* no issue time, expires in future */
4705 intervalString(buf1, suspended->expires-now, user->handle_info);
4706 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4708 case 2: /* no issue time, expired */
4709 intervalString(buf1, now-suspended->expires, user->handle_info);
4710 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4712 case 3: /* no issue time, revoked */
4713 intervalString(buf1, now-suspended->revoked, user->handle_info);
4714 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4716 case 4: /* issue time set, indefinite expiration */
4717 intervalString(buf1, now-suspended->issued, user->handle_info);
4718 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4720 case 5: /* issue time set, expires in future */
4721 intervalString(buf1, now-suspended->issued, user->handle_info);
4722 intervalString(buf2, suspended->expires-now, user->handle_info);
4723 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4725 case 6: /* issue time set, expired */
4726 intervalString(buf1, now-suspended->issued, user->handle_info);
4727 intervalString(buf2, now-suspended->expires, user->handle_info);
4728 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4730 case 7: /* issue time set, revoked */
4731 intervalString(buf1, now-suspended->issued, user->handle_info);
4732 intervalString(buf2, now-suspended->revoked, user->handle_info);
4733 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4736 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4742 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4745 const char *fmt = "%a %b %d %H:%M %Y";
4746 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4748 if(giveownership->staff_issuer)
4750 if(giveownership->reason)
4751 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4752 giveownership->target, giveownership->target_access,
4753 giveownership->staff_issuer, buf, giveownership->reason);
4755 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4756 giveownership->target, giveownership->target_access,
4757 giveownership->staff_issuer, buf);
4761 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4765 static CHANSERV_FUNC(cmd_info)
4767 char modes[MAXLEN], buffer[INTERVALLEN];
4768 struct userData *uData, *owner;
4769 struct chanData *cData;
4770 struct do_not_register *dnr;
4775 cData = channel->channel_info;
4776 reply("CSMSG_CHANNEL_INFO", channel->name);
4778 uData = GetChannelUser(cData, user->handle_info);
4779 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4781 mod_chanmode_format(&cData->modes, modes);
4782 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4783 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4786 for(it = dict_first(cData->notes); it; it = iter_next(it))
4790 note = iter_data(it);
4791 if(!note_type_visible_to_user(cData, note->type, user))
4794 padding = PADLEN - 1 - strlen(iter_key(it));
4795 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4798 if(cData->max_time) {
4799 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4801 reply("CSMSG_CHANNEL_MAX", cData->max);
4803 for(owner = cData->users; owner; owner = owner->next)
4804 if(owner->access == UL_OWNER)
4805 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4806 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4807 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4808 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4810 privileged = IsStaff(user);
4812 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4813 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4814 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4816 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4817 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4819 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4821 struct suspended *suspended;
4822 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4823 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4824 show_suspension_info(cmd, user, suspended);
4826 else if(IsSuspended(cData))
4828 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4829 show_suspension_info(cmd, user, cData->suspended);
4832 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4834 struct giveownership *giveownership;
4835 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4836 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4837 show_giveownership_info(cmd, user, giveownership);
4842 static CHANSERV_FUNC(cmd_netinfo)
4844 extern unsigned long boot_time;
4845 extern unsigned long burst_length;
4846 char interval[INTERVALLEN];
4848 reply("CSMSG_NETWORK_INFO");
4849 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4850 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4851 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4852 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4853 reply("CSMSG_NETWORK_BANS", banCount);
4854 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4855 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4856 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4861 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4863 struct helpfile_table table;
4865 struct userNode *user;
4870 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4871 table.contents = alloca(list->used*sizeof(*table.contents));
4872 for(nn=0; nn<list->used; nn++)
4874 user = list->list[nn];
4875 if(user->modes & skip_flags)
4881 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4884 nick = alloca(strlen(user->nick)+3);
4885 sprintf(nick, "(%s)", user->nick);
4889 table.contents[table.length][0] = nick;
4892 table_send(chanserv, to->nick, 0, NULL, table);
4895 static CHANSERV_FUNC(cmd_ircops)
4897 reply("CSMSG_STAFF_OPERS");
4898 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4902 static CHANSERV_FUNC(cmd_helpers)
4904 reply("CSMSG_STAFF_HELPERS");
4905 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4909 static CHANSERV_FUNC(cmd_staff)
4911 reply("CSMSG_NETWORK_STAFF");
4912 cmd_ircops(CSFUNC_ARGS);
4913 cmd_helpers(CSFUNC_ARGS);
4917 static CHANSERV_FUNC(cmd_peek)
4919 struct modeNode *mn;
4920 char modes[MODELEN];
4922 struct helpfile_table table;
4923 int opcount = 0, voicecount = 0, srvcount = 0;
4925 irc_make_chanmode(channel, modes);
4927 reply("CSMSG_PEEK_INFO", channel->name);
4928 reply("CSMSG_PEEK_TOPIC", channel->topic);
4929 reply("CSMSG_PEEK_MODES", modes);
4933 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4934 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4935 for(n = 0; n < channel->members.used; n++)
4937 mn = channel->members.list[n];
4938 if(IsLocal(mn->user))
4940 else if(mn->modes & MODE_CHANOP)
4942 else if(mn->modes & MODE_VOICE)
4945 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4947 table.contents[table.length] = alloca(sizeof(**table.contents));
4948 table.contents[table.length][0] = mn->user->nick;
4952 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4953 (channel->members.used - opcount - voicecount - srvcount));
4957 reply("CSMSG_PEEK_OPS");
4958 table_send(chanserv, user->nick, 0, NULL, table);
4961 reply("CSMSG_PEEK_NO_OPS");
4965 static MODCMD_FUNC(cmd_wipeinfo)
4967 struct handle_info *victim;
4968 struct userData *ud, *actor, *real_actor;
4969 unsigned int override = 0;
4972 actor = GetChannelUser(channel->channel_info, user->handle_info);
4973 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4974 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4976 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4978 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4981 if((ud->access >= actor->access) && (ud != actor))
4983 reply("MSG_USER_OUTRANKED", victim->handle);
4986 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4987 override = CMD_LOG_OVERRIDE;
4991 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4992 return 1 | override;
4995 static CHANSERV_FUNC(cmd_resync)
4997 struct mod_chanmode *changes;
4998 struct chanData *cData = channel->channel_info;
4999 unsigned int ii, used;
5001 changes = mod_chanmode_alloc(channel->members.used * 2);
5002 for(ii = used = 0; ii < channel->members.used; ++ii)
5004 struct modeNode *mn = channel->members.list[ii];
5005 struct userData *uData;
5007 if(IsService(mn->user))
5010 uData = GetChannelAccess(cData, mn->user->handle_info);
5011 if(!cData->lvlOpts[lvlGiveOps]
5012 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
5014 if(!(mn->modes & MODE_CHANOP))
5016 if(!uData || IsUserAutoOp(uData))
5018 changes->args[used].mode = MODE_CHANOP;
5019 changes->args[used++].u.member = mn;
5020 if(!(mn->modes & MODE_VOICE))
5022 changes->args[used].mode = MODE_VOICE;
5023 changes->args[used++].u.member = mn;
5028 else if(!cData->lvlOpts[lvlGiveVoice]
5029 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5031 if(mn->modes & MODE_CHANOP)
5033 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5034 changes->args[used++].u.member = mn;
5036 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5038 changes->args[used].mode = MODE_VOICE;
5039 changes->args[used++].u.member = mn;
5046 changes->args[used].mode = MODE_REMOVE | mn->modes;
5047 changes->args[used++].u.member = mn;
5051 changes->argc = used;
5052 modcmd_chanmode_announce(changes);
5053 mod_chanmode_free(changes);
5054 reply("CSMSG_RESYNCED_USERS", channel->name);
5058 static CHANSERV_FUNC(cmd_seen)
5060 struct userData *uData;
5061 struct handle_info *handle;
5062 char seen[INTERVALLEN];
5066 if(!irccasecmp(argv[1], chanserv->nick))
5068 reply("CSMSG_IS_CHANSERV");
5072 if(!(handle = get_handle_info(argv[1])))
5074 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5078 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5080 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5085 reply("CSMSG_USER_PRESENT", handle->handle);
5086 else if(uData->seen)
5087 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5089 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5091 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5092 reply("CSMSG_USER_VACATION", handle->handle);
5097 static MODCMD_FUNC(cmd_names)
5099 struct userNode *targ;
5100 struct userData *targData;
5101 unsigned int ii, pos;
5104 for(ii=pos=0; ii<channel->members.used; ++ii)
5106 targ = channel->members.list[ii]->user;
5107 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5110 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5113 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5117 if(IsUserSuspended(targData))
5119 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5122 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5123 reply("CSMSG_END_NAMES", channel->name);
5128 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5130 switch(ntype->visible_type)
5132 case NOTE_VIS_ALL: return 1;
5133 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5134 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5139 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5141 struct userData *uData;
5143 switch(ntype->set_access_type)
5145 case NOTE_SET_CHANNEL_ACCESS:
5146 if(!user->handle_info)
5148 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5150 return uData->access >= ntype->set_access.min_ulevel;
5151 case NOTE_SET_CHANNEL_SETTER:
5152 return check_user_level(channel, user, lvlSetters, 1, 0);
5153 case NOTE_SET_PRIVILEGED: default:
5154 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5158 static CHANSERV_FUNC(cmd_note)
5160 struct chanData *cData;
5162 struct note_type *ntype;
5164 cData = channel->channel_info;
5167 reply("CSMSG_NOT_REGISTERED", channel->name);
5171 /* If no arguments, show all visible notes for the channel. */
5177 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5179 note = iter_data(it);
5180 if(!note_type_visible_to_user(cData, note->type, user))
5183 reply("CSMSG_NOTELIST_HEADER", channel->name);
5184 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5187 reply("CSMSG_NOTELIST_END", channel->name);
5189 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5191 /* If one argument, show the named note. */
5194 if((note = dict_find(cData->notes, argv[1], NULL))
5195 && note_type_visible_to_user(cData, note->type, user))
5197 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5199 else if((ntype = dict_find(note_types, argv[1], NULL))
5200 && note_type_visible_to_user(NULL, ntype, user))
5202 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5207 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5211 /* Assume they're trying to set a note. */
5215 ntype = dict_find(note_types, argv[1], NULL);
5218 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5221 else if(note_type_settable_by_user(channel, ntype, user))
5223 note_text = unsplit_string(argv+2, argc-2, NULL);
5224 if((note = dict_find(cData->notes, argv[1], NULL)))
5225 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5226 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5227 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5229 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5231 /* The note is viewable to staff only, so return 0
5232 to keep the invocation from getting logged (or
5233 regular users can see it in !events). */
5239 reply("CSMSG_NO_ACCESS");
5246 static CHANSERV_FUNC(cmd_delnote)
5251 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5252 || !note_type_settable_by_user(channel, note->type, user))
5254 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5257 dict_remove(channel->channel_info->notes, note->type->name);
5258 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5262 static CHANSERV_FUNC(cmd_events)
5264 struct logSearch discrim;
5265 struct logReport report;
5266 unsigned int matches, limit;
5268 limit = (argc > 1) ? atoi(argv[1]) : 10;
5269 if(limit < 1 || limit > 200)
5272 memset(&discrim, 0, sizeof(discrim));
5273 discrim.masks.bot = chanserv;
5274 discrim.masks.channel_name = channel->name;
5276 discrim.masks.command = argv[2];
5277 discrim.limit = limit;
5278 discrim.max_time = INT_MAX;
5279 discrim.severities = 1 << LOG_COMMAND;
5280 report.reporter = chanserv;
5282 reply("CSMSG_EVENT_SEARCH_RESULTS");
5283 matches = log_entry_search(&discrim, log_report_entry, &report);
5285 reply("MSG_MATCH_COUNT", matches);
5287 reply("MSG_NO_MATCHES");
5291 static CHANSERV_FUNC(cmd_say)
5297 msg = unsplit_string(argv + 1, argc - 1, NULL);
5298 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5300 else if(*argv[1] == '*' && argv[1][1] != '\0')
5302 struct handle_info *hi;
5303 struct userNode *authed;
5306 msg = unsplit_string(argv + 2, argc - 2, NULL);
5308 if (!(hi = get_handle_info(argv[1] + 1)))
5310 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5314 for (authed = hi->users; authed; authed = authed->next_authed)
5315 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5317 else if(GetUserH(argv[1]))
5320 msg = unsplit_string(argv + 2, argc - 2, NULL);
5321 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5325 reply("MSG_NOT_TARGET_NAME");
5331 static CHANSERV_FUNC(cmd_emote)
5337 /* CTCP is so annoying. */
5338 msg = unsplit_string(argv + 1, argc - 1, NULL);
5339 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5341 else if(*argv[1] == '*' && argv[1][1] != '\0')
5343 struct handle_info *hi;
5344 struct userNode *authed;
5347 msg = unsplit_string(argv + 2, argc - 2, NULL);
5349 if (!(hi = get_handle_info(argv[1] + 1)))
5351 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5355 for (authed = hi->users; authed; authed = authed->next_authed)
5356 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5358 else if(GetUserH(argv[1]))
5360 msg = unsplit_string(argv + 2, argc - 2, NULL);
5361 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5365 reply("MSG_NOT_TARGET_NAME");
5371 struct channelList *
5372 chanserv_support_channels(void)
5374 return &chanserv_conf.support_channels;
5377 static CHANSERV_FUNC(cmd_expire)
5379 int channel_count = registered_channels;
5380 expire_channels(chanserv);
5381 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5386 chanserv_expire_suspension(void *data)
5388 struct suspended *suspended = data;
5389 struct chanNode *channel;
5392 /* Update the channel registration data structure. */
5393 if(!suspended->expires || (now < suspended->expires))
5394 suspended->revoked = now;
5395 channel = suspended->cData->channel;
5396 suspended->cData->channel = channel;
5397 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5399 /* If appropriate, re-join ChanServ to the channel. */
5400 if(!IsOffChannel(suspended->cData))
5402 spamserv_cs_suspend(channel, 0, 0, NULL);
5403 ss_cs_join_channel(channel, 1);
5406 /* Mark everyone currently in the channel as present. */
5407 for(ii = 0; ii < channel->members.used; ++ii)
5409 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5418 static CHANSERV_FUNC(cmd_csuspend)
5420 struct suspended *suspended;
5421 char reason[MAXLEN];
5422 unsigned long expiry, duration;
5423 struct userData *uData;
5427 if(IsProtected(channel->channel_info))
5429 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5433 if(argv[1][0] == '!')
5435 else if(IsSuspended(channel->channel_info))
5437 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5438 show_suspension_info(cmd, user, channel->channel_info->suspended);
5442 if(!strcmp(argv[1], "0"))
5444 else if((duration = ParseInterval(argv[1])))
5445 expiry = now + duration;
5448 reply("MSG_INVALID_DURATION", argv[1]);
5452 unsplit_string(argv + 2, argc - 2, reason);
5454 suspended = calloc(1, sizeof(*suspended));
5455 suspended->revoked = 0;
5456 suspended->issued = now;
5457 suspended->suspender = strdup(user->handle_info->handle);
5458 suspended->expires = expiry;
5459 suspended->reason = strdup(reason);
5460 suspended->cData = channel->channel_info;
5461 suspended->previous = suspended->cData->suspended;
5462 suspended->cData->suspended = suspended;
5464 if(suspended->expires)
5465 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5467 if(IsSuspended(channel->channel_info))
5469 suspended->previous->revoked = now;
5470 if(suspended->previous->expires)
5471 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5472 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5473 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5477 /* Mark all users in channel as absent. */
5478 for(uData = channel->channel_info->users; uData; uData = uData->next)
5487 /* Mark the channel as suspended, then part. */
5488 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5489 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5490 DelChannelUser(chanserv, channel, suspended->reason, 0);
5491 reply("CSMSG_SUSPENDED", channel->name);
5492 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5493 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5498 static CHANSERV_FUNC(cmd_cunsuspend)
5500 struct suspended *suspended;
5501 char message[MAXLEN];
5503 if(!IsSuspended(channel->channel_info))
5505 reply("CSMSG_NOT_SUSPENDED", channel->name);
5509 suspended = channel->channel_info->suspended;
5511 /* Expire the suspension and join ChanServ to the channel. */
5512 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5513 chanserv_expire_suspension(suspended);
5514 reply("CSMSG_UNSUSPENDED", channel->name);
5515 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5516 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5520 typedef struct chanservSearch
5525 unsigned long unvisited;
5526 unsigned long registered;
5528 unsigned long flags;
5532 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5535 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5540 search = malloc(sizeof(struct chanservSearch));
5541 memset(search, 0, sizeof(*search));
5544 for(i = 0; i < argc; i++)
5546 /* Assume all criteria require arguments. */
5549 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5553 if(!irccasecmp(argv[i], "name"))
5554 search->name = argv[++i];
5555 else if(!irccasecmp(argv[i], "registrar"))
5556 search->registrar = argv[++i];
5557 else if(!irccasecmp(argv[i], "unvisited"))
5558 search->unvisited = ParseInterval(argv[++i]);
5559 else if(!irccasecmp(argv[i], "registered"))
5560 search->registered = ParseInterval(argv[++i]);
5561 else if(!irccasecmp(argv[i], "flags"))
5564 if(!irccasecmp(argv[i], "nodelete"))
5565 search->flags |= CHANNEL_NODELETE;
5566 else if(!irccasecmp(argv[i], "suspended"))
5567 search->flags |= CHANNEL_SUSPENDED;
5568 else if(!irccasecmp(argv[i], "unreviewed"))
5569 search->flags |= CHANNEL_UNREVIEWED;
5572 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5576 else if(!irccasecmp(argv[i], "limit"))
5577 search->limit = strtoul(argv[++i], NULL, 10);
5580 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5585 if(search->name && !strcmp(search->name, "*"))
5587 if(search->registrar && !strcmp(search->registrar, "*"))
5588 search->registrar = 0;
5597 chanserv_channel_match(struct chanData *channel, search_t search)
5599 const char *name = channel->channel->name;
5600 if((search->name && !match_ircglob(name, search->name)) ||
5601 (search->registrar && !channel->registrar) ||
5602 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5603 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5604 (search->registered && (now - channel->registered) > search->registered) ||
5605 (search->flags && ((search->flags & channel->flags) != search->flags)))
5612 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5614 struct chanData *channel;
5615 unsigned int matches = 0;
5617 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5619 if(!chanserv_channel_match(channel, search))
5629 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5634 search_print(struct chanData *channel, void *data)
5636 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5639 static CHANSERV_FUNC(cmd_search)
5642 unsigned int matches;
5643 channel_search_func action;
5647 if(!irccasecmp(argv[1], "count"))
5648 action = search_count;
5649 else if(!irccasecmp(argv[1], "print"))
5650 action = search_print;
5653 reply("CSMSG_ACTION_INVALID", argv[1]);
5657 search = chanserv_search_create(user, argc - 2, argv + 2);
5661 if(action == search_count)
5662 search->limit = INT_MAX;
5664 if(action == search_print)
5665 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5667 matches = chanserv_channel_search(search, action, user);
5670 reply("MSG_MATCH_COUNT", matches);
5672 reply("MSG_NO_MATCHES");
5678 static CHANSERV_FUNC(cmd_unvisited)
5680 struct chanData *cData;
5681 unsigned long interval = chanserv_conf.channel_expire_delay;
5682 char buffer[INTERVALLEN];
5683 unsigned int limit = 25, matches = 0;
5687 interval = ParseInterval(argv[1]);
5689 limit = atoi(argv[2]);
5692 intervalString(buffer, interval, user->handle_info);
5693 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5695 for(cData = channelList; cData && matches < limit; cData = cData->next)
5697 if((now - cData->visited) < interval)
5700 intervalString(buffer, now - cData->visited, user->handle_info);
5701 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5708 static MODCMD_FUNC(chan_opt_defaulttopic)
5714 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5716 reply("CSMSG_TOPIC_LOCKED", channel->name);
5720 topic = unsplit_string(argv+1, argc-1, NULL);
5722 free(channel->channel_info->topic);
5723 if(topic[0] == '*' && topic[1] == 0)
5725 topic = channel->channel_info->topic = NULL;
5729 topic = channel->channel_info->topic = strdup(topic);
5730 if(channel->channel_info->topic_mask
5731 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5732 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5734 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5737 if(channel->channel_info->topic)
5738 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5740 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5744 static MODCMD_FUNC(chan_opt_topicmask)
5748 struct chanData *cData = channel->channel_info;
5751 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5753 reply("CSMSG_TOPIC_LOCKED", channel->name);
5757 mask = unsplit_string(argv+1, argc-1, NULL);
5759 if(cData->topic_mask)
5760 free(cData->topic_mask);
5761 if(mask[0] == '*' && mask[1] == 0)
5763 cData->topic_mask = 0;
5767 cData->topic_mask = strdup(mask);
5769 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5770 else if(!match_ircglob(cData->topic, cData->topic_mask))
5771 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5775 if(channel->channel_info->topic_mask)
5776 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5778 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5782 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5786 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5790 if(greeting[0] == '*' && greeting[1] == 0)
5794 unsigned int length = strlen(greeting);
5795 if(length > chanserv_conf.greeting_length)
5797 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5800 *data = strdup(greeting);
5809 reply(name, user_find_message(user, "MSG_NONE"));
5813 static MODCMD_FUNC(chan_opt_greeting)
5815 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5818 static MODCMD_FUNC(chan_opt_usergreeting)
5820 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5823 static MODCMD_FUNC(chan_opt_modes)
5825 struct mod_chanmode *new_modes;
5830 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5832 reply("CSMSG_NO_ACCESS");
5835 if(argv[1][0] == '*' && argv[1][1] == 0)
5837 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5839 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)))
5841 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5844 else if(new_modes->argc > 1)
5846 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5847 mod_chanmode_free(new_modes);
5852 channel->channel_info->modes = *new_modes;
5853 modcmd_chanmode_announce(new_modes);
5854 mod_chanmode_free(new_modes);
5858 mod_chanmode_format(&channel->channel_info->modes, modes);
5860 reply("CSMSG_SET_MODES", modes);
5862 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5866 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5868 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5870 struct chanData *cData = channel->channel_info;
5875 /* Set flag according to value. */
5876 if(enabled_string(argv[1]))
5878 cData->flags |= mask;
5881 else if(disabled_string(argv[1]))
5883 cData->flags &= ~mask;
5888 reply("MSG_INVALID_BINARY", argv[1]);
5894 /* Find current option value. */
5895 value = (cData->flags & mask) ? 1 : 0;
5899 reply(name, user_find_message(user, "MSG_ON"));
5901 reply(name, user_find_message(user, "MSG_OFF"));
5905 static MODCMD_FUNC(chan_opt_nodelete)
5907 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5909 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5913 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5916 static MODCMD_FUNC(chan_opt_dynlimit)
5918 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5921 static MODCMD_FUNC(chan_opt_advtopic)
5923 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5926 static MODCMD_FUNC(chan_opt_offchannel)
5928 struct chanData *cData = channel->channel_info;
5933 /* Set flag according to value. */
5934 if(enabled_string(argv[1]))
5936 if(!IsOffChannel(cData))
5937 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5938 cData->flags |= CHANNEL_OFFCHANNEL;
5941 else if(disabled_string(argv[1]))
5943 if(IsOffChannel(cData))
5945 struct mod_chanmode change;
5946 mod_chanmode_init(&change);
5948 change.args[0].mode = MODE_CHANOP;
5949 change.args[0].u.member = AddChannelUser(chanserv, channel);
5950 mod_chanmode_announce(chanserv, channel, &change);
5952 cData->flags &= ~CHANNEL_OFFCHANNEL;
5957 reply("MSG_INVALID_BINARY", argv[1]);
5963 /* Find current option value. */
5964 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5968 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5970 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5974 static MODCMD_FUNC(chan_opt_unreviewed)
5976 struct chanData *cData = channel->channel_info;
5977 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5983 /* The two directions can have different ACLs. */
5984 if(enabled_string(argv[1]))
5986 else if(disabled_string(argv[1]))
5990 reply("MSG_INVALID_BINARY", argv[1]);
5994 if (new_value != value)
5996 struct svccmd *subcmd;
5997 char subcmd_name[32];
5999 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
6000 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
6003 reply("MSG_COMMAND_DISABLED", subcmd_name);
6006 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6010 cData->flags |= CHANNEL_UNREVIEWED;
6013 free(cData->registrar);
6014 cData->registrar = strdup(user->handle_info->handle);
6015 cData->flags &= ~CHANNEL_UNREVIEWED;
6022 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6024 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6028 static MODCMD_FUNC(chan_opt_defaults)
6030 struct userData *uData;
6031 struct chanData *cData;
6032 const char *confirm;
6033 enum levelOption lvlOpt;
6034 enum charOption chOpt;
6036 cData = channel->channel_info;
6037 uData = GetChannelUser(cData, user->handle_info);
6038 if(!uData || (uData->access < UL_OWNER))
6040 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6043 confirm = make_confirmation_string(uData);
6044 if((argc < 2) || strcmp(argv[1], confirm))
6046 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6049 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6050 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6051 cData->modes = chanserv_conf.default_modes;
6052 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6053 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6054 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6055 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6056 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6061 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6063 struct chanData *cData = channel->channel_info;
6064 struct userData *uData;
6065 unsigned short value;
6069 if(!check_user_level(channel, user, option, 1, 1))
6071 reply("CSMSG_CANNOT_SET");
6074 value = user_level_from_name(argv[1], UL_OWNER+1);
6075 if(!value && strcmp(argv[1], "0"))
6077 reply("CSMSG_INVALID_ACCESS", argv[1]);
6080 uData = GetChannelUser(cData, user->handle_info);
6081 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6083 reply("CSMSG_BAD_SETLEVEL");
6089 if(value > cData->lvlOpts[lvlGiveOps])
6091 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6096 if(value < cData->lvlOpts[lvlGiveVoice])
6098 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6103 /* This test only applies to owners, since non-owners
6104 * trying to set an option to above their level get caught
6105 * by the CSMSG_BAD_SETLEVEL test above.
6107 if(value > uData->access)
6109 reply("CSMSG_BAD_SETTERS");
6116 cData->lvlOpts[option] = value;
6118 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6122 static MODCMD_FUNC(chan_opt_enfops)
6124 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6127 static MODCMD_FUNC(chan_opt_giveops)
6129 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6132 static MODCMD_FUNC(chan_opt_enfmodes)
6134 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6137 static MODCMD_FUNC(chan_opt_enftopic)
6139 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6142 static MODCMD_FUNC(chan_opt_pubcmd)
6144 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6147 static MODCMD_FUNC(chan_opt_setters)
6149 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6152 static MODCMD_FUNC(chan_opt_ctcpusers)
6154 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6157 static MODCMD_FUNC(chan_opt_userinfo)
6159 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6162 static MODCMD_FUNC(chan_opt_givevoice)
6164 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6167 static MODCMD_FUNC(chan_opt_topicsnarf)
6169 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6172 static MODCMD_FUNC(chan_opt_vote)
6174 return channel_level_option(lvlVote, CSFUNC_ARGS);
6177 static MODCMD_FUNC(chan_opt_inviteme)
6179 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6183 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6185 struct chanData *cData = channel->channel_info;
6186 int count = charOptions[option].count, idx;
6190 idx = atoi(argv[1]);
6192 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6194 reply("CSMSG_INVALID_NUMERIC", idx);
6195 /* Show possible values. */
6196 for(idx = 0; idx < count; idx++)
6197 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6201 cData->chOpts[option] = charOptions[option].values[idx].value;
6205 /* Find current option value. */
6208 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6212 /* Somehow, the option value is corrupt; reset it to the default. */
6213 cData->chOpts[option] = charOptions[option].default_value;
6218 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6222 static MODCMD_FUNC(chan_opt_protect)
6224 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6227 static MODCMD_FUNC(chan_opt_toys)
6229 return channel_multiple_option(chToys, CSFUNC_ARGS);
6232 static MODCMD_FUNC(chan_opt_ctcpreaction)
6234 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6237 static MODCMD_FUNC(chan_opt_topicrefresh)
6239 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6242 static struct svccmd_list set_shows_list;
6245 handle_svccmd_unbind(struct svccmd *target) {
6247 for(ii=0; ii<set_shows_list.used; ++ii)
6248 if(target == set_shows_list.list[ii])
6249 set_shows_list.used = 0;
6252 static CHANSERV_FUNC(cmd_set)
6254 struct svccmd *subcmd;
6258 /* Check if we need to (re-)initialize set_shows_list. */
6259 if(!set_shows_list.used)
6261 if(!set_shows_list.size)
6263 set_shows_list.size = chanserv_conf.set_shows->used;
6264 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6266 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6268 const char *name = chanserv_conf.set_shows->list[ii];
6269 sprintf(buf, "%s %s", argv[0], name);
6270 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6273 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6276 svccmd_list_append(&set_shows_list, subcmd);
6282 reply("CSMSG_CHANNEL_OPTIONS");
6283 for(ii = 0; ii < set_shows_list.used; ii++)
6285 subcmd = set_shows_list.list[ii];
6286 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6291 sprintf(buf, "%s %s", argv[0], argv[1]);
6292 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6295 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6298 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6300 reply("CSMSG_NO_ACCESS");
6306 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6310 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6312 struct userData *uData;
6314 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6317 reply("CSMSG_NOT_USER", channel->name);
6323 /* Just show current option value. */
6325 else if(enabled_string(argv[1]))
6327 uData->flags |= mask;
6329 else if(disabled_string(argv[1]))
6331 uData->flags &= ~mask;
6335 reply("MSG_INVALID_BINARY", argv[1]);
6339 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6343 static MODCMD_FUNC(user_opt_noautoop)
6345 struct userData *uData;
6347 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6350 reply("CSMSG_NOT_USER", channel->name);
6353 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6354 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6356 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6359 static MODCMD_FUNC(user_opt_autoinvite)
6361 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6363 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6365 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6368 static MODCMD_FUNC(user_opt_info)
6370 struct userData *uData;
6373 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6377 /* If they got past the command restrictions (which require access)
6378 * but fail this test, we have some fool with security override on.
6380 reply("CSMSG_NOT_USER", channel->name);
6387 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6388 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6390 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6393 bp = strcspn(infoline, "\001");
6396 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6401 if(infoline[0] == '*' && infoline[1] == 0)
6404 uData->info = strdup(infoline);
6407 reply("CSMSG_USET_INFO", uData->info);
6409 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6413 struct svccmd_list uset_shows_list;
6415 static CHANSERV_FUNC(cmd_uset)
6417 struct svccmd *subcmd;
6421 /* Check if we need to (re-)initialize uset_shows_list. */
6422 if(!uset_shows_list.used)
6426 "NoAutoOp", "AutoInvite", "Info"
6429 if(!uset_shows_list.size)
6431 uset_shows_list.size = ArrayLength(options);
6432 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6434 for(ii = 0; ii < ArrayLength(options); ii++)
6436 const char *name = options[ii];
6437 sprintf(buf, "%s %s", argv[0], name);
6438 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6441 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6444 svccmd_list_append(&uset_shows_list, subcmd);
6450 /* Do this so options are presented in a consistent order. */
6451 reply("CSMSG_USER_OPTIONS");
6452 for(ii = 0; ii < uset_shows_list.used; ii++)
6453 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6457 sprintf(buf, "%s %s", argv[0], argv[1]);
6458 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6461 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6465 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6468 static CHANSERV_FUNC(cmd_giveownership)
6470 struct handle_info *new_owner_hi;
6471 struct userData *new_owner;
6472 struct userData *curr_user;
6473 struct userData *invoker;
6474 struct chanData *cData = channel->channel_info;
6475 struct do_not_register *dnr;
6476 const char *confirm;
6477 struct giveownership *giveownership;
6478 unsigned int force, override;
6479 unsigned short co_access, new_owner_old_access;
6480 char reason[MAXLEN], transfer_reason[MAXLEN];
6483 curr_user = GetChannelAccess(cData, user->handle_info);
6484 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6486 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6487 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6488 && (uData->access > 500)
6489 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6490 || uData->access < 500));
6492 if(!curr_user || (curr_user->access != UL_OWNER))
6494 struct userData *owner = NULL;
6495 for(curr_user = channel->channel_info->users;
6497 curr_user = curr_user->next)
6499 if(curr_user->access != UL_OWNER)
6503 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6510 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6512 char delay[INTERVALLEN];
6513 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6514 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6517 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6519 if(new_owner_hi == user->handle_info)
6521 reply("CSMSG_NO_TRANSFER_SELF");
6524 new_owner = GetChannelAccess(cData, new_owner_hi);
6529 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6533 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6537 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6539 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6542 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6543 if(!IsHelping(user))
6544 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6546 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6549 invoker = GetChannelUser(cData, user->handle_info);
6550 if(invoker->access <= UL_OWNER)
6552 confirm = make_confirmation_string(curr_user);
6553 if((argc < 3) || strcmp(argv[2], confirm))
6555 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6559 new_owner_old_access = new_owner->access;
6560 if(new_owner->access >= UL_COOWNER)
6561 co_access = new_owner->access;
6563 co_access = UL_COOWNER;
6564 new_owner->access = UL_OWNER;
6566 curr_user->access = co_access;
6567 cData->ownerTransfer = now;
6568 giveownership = calloc(1, sizeof(*giveownership));
6569 giveownership->issued = now;
6570 giveownership->old_owner = curr_user->handle->handle;
6571 giveownership->target = new_owner_hi->handle;
6572 giveownership->target_access = new_owner_old_access;
6575 if(argc > (2 + force))
6577 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6578 giveownership->reason = strdup(transfer_reason);
6580 giveownership->staff_issuer = strdup(user->handle_info->handle);
6583 giveownership->previous = channel->channel_info->giveownership;
6584 channel->channel_info->giveownership = giveownership;
6585 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6586 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6587 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6592 chanserv_expire_user_suspension(void *data)
6594 struct userData *target = data;
6596 target->expires = 0;
6597 target->flags &= ~USER_SUSPENDED;
6600 static CHANSERV_FUNC(cmd_suspend)
6602 struct handle_info *hi;
6603 struct userData *actor, *real_actor, *target;
6604 unsigned int override = 0;
6608 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6609 actor = GetChannelUser(channel->channel_info, user->handle_info);
6610 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6611 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6613 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6616 if(target->access >= actor->access)
6618 reply("MSG_USER_OUTRANKED", hi->handle);
6621 if(target->flags & USER_SUSPENDED)
6623 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6628 target->present = 0;
6631 if(!real_actor || target->access >= real_actor->access)
6632 override = CMD_LOG_OVERRIDE;
6633 if(!strcmp(argv[2], "0"))
6637 unsigned int duration;
6638 if(!(duration = ParseInterval(argv[2])))
6640 reply("MSG_INVALID_DURATION", argv[2]);
6643 expiry = now + duration;
6646 target->expires = expiry;
6649 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6651 target->flags |= USER_SUSPENDED;
6652 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6653 return 1 | override;
6656 static CHANSERV_FUNC(cmd_unsuspend)
6658 struct handle_info *hi;
6659 struct userData *actor, *real_actor, *target;
6660 unsigned int override = 0;
6663 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6664 actor = GetChannelUser(channel->channel_info, user->handle_info);
6665 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6666 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6668 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6671 if(target->access >= actor->access)
6673 reply("MSG_USER_OUTRANKED", hi->handle);
6676 if(!(target->flags & USER_SUSPENDED))
6678 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6681 if(!real_actor || target->access >= real_actor->access)
6682 override = CMD_LOG_OVERRIDE;
6683 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
6684 target->flags &= ~USER_SUSPENDED;
6685 scan_user_presence(target, NULL);
6686 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6687 return 1 | override;
6690 static MODCMD_FUNC(cmd_deleteme)
6692 struct handle_info *hi;
6693 struct userData *target;
6694 const char *confirm_string;
6695 unsigned short access_level;
6698 hi = user->handle_info;
6699 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6701 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6704 if(target->access == UL_OWNER)
6706 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6709 confirm_string = make_confirmation_string(target);
6710 if((argc < 2) || strcmp(argv[1], confirm_string))
6712 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6715 access_level = target->access;
6716 channel_name = strdup(channel->name);
6717 del_channel_user(target, 1);
6718 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6723 static CHANSERV_FUNC(cmd_addvote)
6725 struct chanData *cData = channel->channel_info;
6726 struct userData *uData, *target;
6727 struct handle_info *hi;
6728 if (!cData) return 0;
6730 hi = user->handle_info;
6731 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6733 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6736 if(target->access < 300) {
6737 reply("CSMSG_NO_ACCESS");
6741 reply("CSMSG_ADDVOTE_FULL");
6745 msg = unsplit_string(argv + 1, argc - 1, NULL);
6746 cData->vote = strdup(msg);
6747 cData->vote_start=0;
6748 dict_delete(cData->vote_options);
6749 cData->vote_options = dict_new();
6750 dict_set_free_data(cData->vote_options, free_vote_options);
6751 for(uData = channel->channel_info->users; uData; uData = uData->next)
6756 reply("CSMSG_ADDVOTE_DONE");
6760 static CHANSERV_FUNC(cmd_delvote)
6762 struct chanData *cData = channel->channel_info;
6763 struct userData *target;
6764 struct handle_info *hi;
6765 if (!cData) return 0;
6766 hi = user->handle_info;
6767 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6769 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6772 if(target->access < 300) {
6773 reply("CSMSG_NO_ACCESS");
6777 reply("CSMSG_NO_VOTE");
6782 reply("CSMSG_DELVOTE_DONE");
6786 static CHANSERV_FUNC(cmd_addoption)
6788 struct chanData *cData = channel->channel_info;
6789 struct userData *target;
6790 struct handle_info *hi;
6791 if (!cData) return 0;
6793 hi = user->handle_info;
6794 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6796 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6799 if(target->access < 300) {
6800 reply("CSMSG_NO_ACCESS");
6804 reply("CSMSG_NO_VOTE");
6810 msg = unsplit_string(argv + 1, argc - 1, NULL);
6813 unsigned int lastid = 1;
6814 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6815 struct vote_option *cvOpt = iter_data(it);
6816 if(cvOpt->option_id > lastid)
6817 lastid = cvOpt->option_id;
6819 struct vote_option *vOpt;
6820 vOpt = calloc(1, sizeof(*vOpt));
6821 vOpt->name = strdup(msg);
6822 vOpt->option_id = (lastid + 1);
6824 sprintf(str,"%i",(lastid + 1));
6825 vOpt->option_str = strdup(str);
6827 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6829 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6833 static CHANSERV_FUNC(cmd_deloption)
6835 struct chanData *cData = channel->channel_info;
6836 struct userData *uData, *target;
6837 struct handle_info *hi;
6838 if (!cData) return 0;
6840 hi = user->handle_info;
6841 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6843 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6846 if(target->access < 300) {
6847 reply("CSMSG_NO_ACCESS");
6851 reply("CSMSG_NO_VOTE");
6854 if(cData->vote_start) {
6855 if(dict_size(cData->vote_options) < 3) {
6856 reply("CSMSG_VOTE_NEED_OPTIONS");
6861 int find_id = atoi(argv[1]);
6863 unsigned int found = 0;
6866 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6868 if (find_id == ii) {
6869 struct vote_option *vOpt = iter_data(it);
6870 found = vOpt->option_id;
6872 sprintf(str,"%i",vOpt->option_id);
6873 dict_remove(cData->vote_options, str);
6878 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6879 if(uData->votefor == found) {
6884 reply("CSMSG_DELOPTION_DONE");
6887 reply("CSMSG_DELOPTION_NONE");
6892 static CHANSERV_FUNC(cmd_vote)
6894 struct chanData *cData = channel->channel_info;
6895 struct userData *target;
6896 struct handle_info *hi;
6897 unsigned int votedfor = 0;
6898 char *votedfor_str = NULL;
6900 if (!cData || !cData->vote) {
6901 reply("CSMSG_NO_VOTE");
6904 if(argc > 1 && cData->vote_start) {
6905 hi = user->handle_info;
6906 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6908 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6911 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6912 reply("CSMSG_NO_ACCESS");
6916 reply("CSMSG_VOTE_VOTED");
6919 int find_id = atoi(argv[1]);
6922 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6924 if (find_id == ii) {
6925 struct vote_option *vOpt = iter_data(it);
6928 target->votefor = vOpt->option_id;
6929 votedfor = vOpt->option_id;
6930 votedfor_str = vOpt->name;
6934 reply("CSMSG_VOTE_INVALID");
6938 if (!cData->vote_start) {
6939 reply("CSMSG_VOTE_NOT_STARTED");
6941 reply("CSMSG_VOTE_QUESTION",cData->vote);
6943 unsigned int voteid = 0;
6946 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6947 struct vote_option *vOpt = iter_data(it);
6949 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6951 if(argc > 1 && cData->vote_start && votedfor_str) {
6952 reply("CSMSG_VOTE_DONE",votedfor_str);
6957 static CHANSERV_FUNC(cmd_startvote)
6959 struct chanData *cData = channel->channel_info;
6960 struct userData *target;
6961 struct handle_info *hi;
6962 if (!cData) return 0;
6963 hi = user->handle_info;
6964 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6966 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6969 if(target->access < 300) {
6970 reply("CSMSG_NO_ACCESS");
6974 reply("CSMSG_NO_VOTE");
6977 if(cData->vote_start) {
6978 reply("CSMSG_STARTVOTE_RUNNING");
6981 if(dict_size(cData->vote_options) < 2) {
6982 reply("CSMSG_VOTE_NEED_OPTIONS");
6985 cData->vote_start = 1;
6986 char response[MAXLEN];
6987 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6988 irc_privmsg(cmd->parent->bot, channel->name, response);
6989 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6990 irc_privmsg(cmd->parent->bot, channel->name, response);
6991 unsigned int voteid = 0;
6993 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6994 struct vote_option *vOpt = iter_data(it);
6996 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6997 irc_privmsg(cmd->parent->bot, channel->name, response);
6999 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
7000 irc_privmsg(cmd->parent->bot, channel->name, response);
7001 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
7002 irc_privmsg(cmd->parent->bot, channel->name, response);
7006 static CHANSERV_FUNC(cmd_endvote)
7008 struct chanData *cData = channel->channel_info;
7009 struct userData *target;
7010 struct handle_info *hi;
7011 if (!cData) return 0;
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");
7023 reply("CSMSG_NO_VOTE");
7026 if(!cData->vote_start) {
7027 reply("CSMSG_ENDVOTE_STOPPED");
7030 cData->vote_start = 0;
7031 reply("CSMSG_ENDVOTE_DONE");
7035 static CHANSERV_FUNC(cmd_voteresults)
7037 struct chanData *cData = channel->channel_info;
7038 struct userData *target;
7039 struct handle_info *hi;
7040 if (!cData) return 0;
7042 reply("CSMSG_NO_VOTE");
7045 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7046 hi = user->handle_info;
7047 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7049 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7052 if(target->access < 300) {
7053 reply("CSMSG_NO_ACCESS");
7056 char response[MAXLEN];
7057 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7058 irc_privmsg(cmd->parent->bot, channel->name, response);
7059 unsigned int voteid = 0;
7061 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7062 struct vote_option *vOpt = iter_data(it);
7064 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7065 irc_privmsg(cmd->parent->bot, channel->name, response);
7068 reply("CSMSG_VOTE_QUESTION",cData->vote);
7069 unsigned int voteid = 0;
7071 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7072 struct vote_option *vOpt = iter_data(it);
7074 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7081 chanserv_refresh_topics(UNUSED_ARG(void *data))
7083 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7084 struct chanData *cData;
7087 for(cData = channelList; cData; cData = cData->next)
7089 if(IsSuspended(cData))
7091 opt = cData->chOpts[chTopicRefresh];
7094 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7097 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7098 cData->last_refresh = refresh_num;
7100 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7103 static CHANSERV_FUNC(cmd_unf)
7107 char response[MAXLEN];
7108 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7109 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7110 irc_privmsg(cmd->parent->bot, channel->name, response);
7113 reply("CSMSG_UNF_RESPONSE");
7117 static CHANSERV_FUNC(cmd_ping)
7121 char response[MAXLEN];
7122 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7123 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7124 irc_privmsg(cmd->parent->bot, channel->name, response);
7127 reply("CSMSG_PING_RESPONSE");
7131 static CHANSERV_FUNC(cmd_wut)
7135 char response[MAXLEN];
7136 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7137 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7138 irc_privmsg(cmd->parent->bot, channel->name, response);
7141 reply("CSMSG_WUT_RESPONSE");
7145 static CHANSERV_FUNC(cmd_8ball)
7147 unsigned int i, j, accum;
7152 for(i=1; i<argc; i++)
7153 for(j=0; argv[i][j]; j++)
7154 accum = (accum << 5) - accum + toupper(argv[i][j]);
7155 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7158 char response[MAXLEN];
7159 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7160 irc_privmsg(cmd->parent->bot, channel->name, response);
7163 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7167 static CHANSERV_FUNC(cmd_d)
7169 unsigned long sides, count, modifier, ii, total;
7170 char response[MAXLEN], *sep;
7174 if((count = strtoul(argv[1], &sep, 10)) < 1)
7184 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7185 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7189 else if((sep[0] == '-') && isdigit(sep[1]))
7190 modifier = strtoul(sep, NULL, 10);
7191 else if((sep[0] == '+') && isdigit(sep[1]))
7192 modifier = strtoul(sep+1, NULL, 10);
7199 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7204 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7207 for(total = ii = 0; ii < count; ++ii)
7208 total += (rand() % sides) + 1;
7211 if((count > 1) || modifier)
7213 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7214 sprintf(response, fmt, total, count, sides, modifier);
7218 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7219 sprintf(response, fmt, total, sides);
7222 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7224 send_message_type(4, user, cmd->parent->bot, "%s", response);
7228 static CHANSERV_FUNC(cmd_huggle)
7230 /* CTCP must be via PRIVMSG, never notice */
7232 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7234 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7239 chanserv_adjust_limit(void *data)
7241 struct mod_chanmode change;
7242 struct chanData *cData = data;
7243 struct chanNode *channel = cData->channel;
7246 if(IsSuspended(cData))
7249 cData->limitAdjusted = now;
7250 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7251 if(cData->modes.modes_set & MODE_LIMIT)
7253 if(limit > cData->modes.new_limit)
7254 limit = cData->modes.new_limit;
7255 else if(limit == cData->modes.new_limit)
7259 mod_chanmode_init(&change);
7260 change.modes_set = MODE_LIMIT;
7261 change.new_limit = limit;
7262 mod_chanmode_announce(chanserv, channel, &change);
7266 handle_new_channel(struct chanNode *channel)
7268 struct chanData *cData;
7270 if(!(cData = channel->channel_info))
7273 if(cData->modes.modes_set || cData->modes.modes_clear)
7274 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7276 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7277 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7280 void handle_new_channel_created(char *chan, struct userNode *user) {
7281 if(user->handle_info && chanserv_conf.new_channel_authed) {
7282 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7283 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7284 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7286 if(chanserv_conf.new_channel_msg)
7287 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7290 /* Welcome to my worst nightmare. Warning: Read (or modify)
7291 the code below at your own risk. */
7293 handle_join(struct modeNode *mNode)
7295 struct mod_chanmode change;
7296 struct userNode *user = mNode->user;
7297 struct chanNode *channel = mNode->channel;
7298 struct chanData *cData;
7299 struct userData *uData = NULL;
7300 struct banData *bData;
7301 struct handle_info *handle;
7302 unsigned int modes = 0, info = 0;
7306 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7309 cData = channel->channel_info;
7310 if(channel->members.used > cData->max) {
7311 cData->max = channel->members.used;
7312 cData->max_time = now;
7315 for(i = 0; i < channel->invited.used; i++)
7317 if(channel->invited.list[i] == user) {
7318 userList_remove(&channel->invited, user);
7322 /* Check for bans. If they're joining through a ban, one of two
7324 * 1: Join during a netburst, by riding the break. Kick them
7325 * unless they have ops or voice in the channel.
7326 * 2: They're allowed to join through the ban (an invite in
7327 * ircu2.10, or a +e on Hybrid, or something).
7328 * If they're not joining through a ban, and the banlist is not
7329 * full, see if they're on the banlist for the channel. If so,
7332 if(user->uplink->burst && !mNode->modes)
7335 for(ii = 0; ii < channel->banlist.used; ii++)
7337 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7339 /* Riding a netburst. Naughty. */
7340 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7346 mod_chanmode_init(&change);
7348 if(channel->banlist.used < MAXBANS)
7350 /* Not joining through a ban. */
7351 for(bData = cData->bans;
7352 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7353 bData = bData->next);
7357 char kick_reason[MAXLEN];
7358 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7360 bData->triggered = now;
7361 if(bData != cData->bans)
7363 /* Shuffle the ban to the head of the list. */
7365 bData->next->prev = bData->prev;
7367 bData->prev->next = bData->next;
7370 bData->next = cData->bans;
7373 cData->bans->prev = bData;
7374 cData->bans = bData;
7377 change.args[0].mode = MODE_BAN;
7378 change.args[0].u.hostmask = bData->mask;
7379 mod_chanmode_announce(chanserv, channel, &change);
7380 KickChannelUser(user, channel, chanserv, kick_reason);
7385 /* ChanServ will not modify the limits in join-flooded channels,
7386 or when there are enough slots left below the limit. */
7387 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7388 && !channel->join_flooded
7389 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7391 /* The user count has begun "bumping" into the channel limit,
7392 so set a timer to raise the limit a bit. Any previous
7393 timers are removed so three incoming users within the delay
7394 results in one limit change, not three. */
7396 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7397 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7400 if(channel->join_flooded)
7402 /* don't automatically give ops or voice during a join flood */
7404 else if(cData->lvlOpts[lvlGiveOps] == 0)
7405 modes |= MODE_CHANOP;
7406 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7407 modes |= MODE_VOICE;
7409 greeting = cData->greeting;
7410 if(user->handle_info)
7412 handle = user->handle_info;
7414 if(IsHelper(user) && !IsHelping(user))
7417 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7419 if(channel == chanserv_conf.support_channels.list[ii])
7421 HANDLE_SET_FLAG(user->handle_info, HELPING);
7427 uData = GetTrueChannelAccess(cData, handle);
7428 if(uData && !IsUserSuspended(uData))
7430 /* Ops and above were handled by the above case. */
7431 if(IsUserAutoOp(uData))
7433 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7434 modes |= MODE_CHANOP;
7435 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7436 modes |= MODE_VOICE;
7438 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7439 cData->visited = now;
7440 if(cData->user_greeting)
7441 greeting = cData->user_greeting;
7443 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7444 && ((now - uData->seen) >= chanserv_conf.info_delay)
7452 /* If user joining normally (not during burst), apply op or voice,
7453 * and send greeting/userinfo as appropriate.
7455 if(!user->uplink->burst)
7459 if(modes & MODE_CHANOP)
7460 modes &= ~MODE_VOICE;
7461 change.args[0].mode = modes;
7462 change.args[0].u.member = mNode;
7463 mod_chanmode_announce(chanserv, channel, &change);
7466 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7467 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7468 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7474 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7476 struct mod_chanmode change;
7477 struct userData *channel;
7478 unsigned int ii, jj;
7480 if(!user->handle_info)
7483 mod_chanmode_init(&change);
7485 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7487 struct chanNode *cn;
7488 struct modeNode *mn;
7489 if(IsUserSuspended(channel)
7490 || IsSuspended(channel->channel)
7491 || !(cn = channel->channel->channel))
7494 mn = GetUserMode(cn, user);
7497 if(!IsUserSuspended(channel)
7498 && IsUserAutoInvite(channel)
7499 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7501 && !user->uplink->burst)
7502 irc_invite(chanserv, user, cn);
7506 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7507 channel->channel->visited = now;
7509 if(IsUserAutoOp(channel))
7511 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7512 change.args[0].mode = MODE_CHANOP;
7513 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7514 change.args[0].mode = MODE_VOICE;
7516 change.args[0].mode = 0;
7517 change.args[0].u.member = mn;
7518 if(change.args[0].mode)
7519 mod_chanmode_announce(chanserv, cn, &change);
7522 channel->seen = now;
7523 channel->present = 1;
7526 for(ii = 0; ii < user->channels.used; ++ii)
7528 struct chanNode *chan = user->channels.list[ii]->channel;
7529 struct banData *ban;
7531 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7532 || !chan->channel_info
7533 || IsSuspended(chan->channel_info))
7535 for(jj = 0; jj < chan->banlist.used; ++jj)
7536 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7538 if(jj < chan->banlist.used)
7540 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7542 char kick_reason[MAXLEN];
7543 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7545 change.args[0].mode = MODE_BAN;
7546 change.args[0].u.hostmask = ban->mask;
7547 mod_chanmode_announce(chanserv, chan, &change);
7548 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7549 KickChannelUser(user, chan, chanserv, kick_reason);
7550 ban->triggered = now;
7555 if(IsSupportHelper(user))
7557 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7559 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7561 HANDLE_SET_FLAG(user->handle_info, HELPING);
7569 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7571 struct chanData *cData;
7572 struct userData *uData;
7574 cData = mn->channel->channel_info;
7575 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7578 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7580 /* Allow for a bit of padding so that the limit doesn't
7581 track the user count exactly, which could get annoying. */
7582 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7584 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7585 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7589 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7591 scan_user_presence(uData, mn->user);
7593 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7594 cData->visited = now;
7597 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7600 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7601 struct chanNode *channel;
7602 struct userNode *exclude;
7603 /* When looking at the channel that is being /part'ed, we
7604 * have to skip over the client that is leaving. For
7605 * other channels, we must not do that.
7607 channel = chanserv_conf.support_channels.list[ii];
7608 exclude = (channel == mn->channel) ? mn->user : NULL;
7609 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7612 if(ii == chanserv_conf.support_channels.used)
7613 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7618 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7620 struct userData *uData;
7622 if(!channel->channel_info || !kicker || IsService(kicker)
7623 || (kicker == victim) || IsSuspended(channel->channel_info)
7624 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7627 if(protect_user(victim, kicker, channel->channel_info))
7629 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7630 KickChannelUser(kicker, channel, chanserv, reason);
7633 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7638 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7640 struct chanData *cData;
7642 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7645 cData = channel->channel_info;
7646 if(bad_topic(channel, user, channel->topic))
7648 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7649 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7650 SetChannelTopic(channel, chanserv, old_topic, 1);
7651 else if(cData->topic)
7652 SetChannelTopic(channel, chanserv, cData->topic, 1);
7655 /* With topicsnarf, grab the topic and save it as the default topic. */
7656 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7659 cData->topic = strdup(channel->topic);
7665 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7667 struct mod_chanmode *bounce = NULL;
7668 unsigned int bnc, ii;
7671 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7674 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7675 && mode_lock_violated(&channel->channel_info->modes, change))
7677 char correct[MAXLEN];
7678 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7679 mod_chanmode_format(&channel->channel_info->modes, correct);
7680 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7682 for(ii = bnc = 0; ii < change->argc; ++ii)
7684 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7686 const struct userNode *victim = change->args[ii].u.member->user;
7687 if(!protect_user(victim, user, channel->channel_info))
7690 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7693 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7694 bounce->args[bnc].u.member = GetUserMode(channel, user);
7695 if(bounce->args[bnc].u.member)
7699 bounce->args[bnc].mode = MODE_CHANOP;
7700 bounce->args[bnc].u.member = change->args[ii].u.member;
7702 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7704 else if(change->args[ii].mode & MODE_CHANOP)
7706 const struct userNode *victim = change->args[ii].u.member->user;
7707 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7710 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7711 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7712 bounce->args[bnc].u.member = change->args[ii].u.member;
7715 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7717 const char *ban = change->args[ii].u.hostmask;
7718 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7721 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7722 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7723 bounce->args[bnc].u.hostmask = strdup(ban);
7725 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7730 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7731 mod_chanmode_announce(chanserv, channel, bounce);
7732 for(ii = 0; ii < change->argc; ++ii)
7733 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7734 free((char*)bounce->args[ii].u.hostmask);
7735 mod_chanmode_free(bounce);
7740 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7742 struct chanNode *channel;
7743 struct banData *bData;
7744 struct mod_chanmode change;
7745 unsigned int ii, jj;
7746 char kick_reason[MAXLEN];
7748 mod_chanmode_init(&change);
7750 change.args[0].mode = MODE_BAN;
7751 for(ii = 0; ii < user->channels.used; ++ii)
7753 channel = user->channels.list[ii]->channel;
7754 /* Need not check for bans if they're opped or voiced. */
7755 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7757 /* Need not check for bans unless channel registration is active. */
7758 if(!channel->channel_info || IsSuspended(channel->channel_info))
7760 /* Look for a matching ban already on the channel. */
7761 for(jj = 0; jj < channel->banlist.used; ++jj)
7762 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7764 /* Need not act if we found one. */
7765 if(jj < channel->banlist.used)
7767 /* Look for a matching ban in this channel. */
7768 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7770 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7772 change.args[0].u.hostmask = bData->mask;
7773 mod_chanmode_announce(chanserv, channel, &change);
7774 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7775 KickChannelUser(user, channel, chanserv, kick_reason);
7776 bData->triggered = now;
7777 break; /* we don't need to check any more bans in the channel */
7782 static void handle_rename(struct handle_info *handle, const char *old_handle)
7784 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7788 dict_remove2(handle_dnrs, old_handle, 1);
7789 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7790 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7795 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7797 struct userNode *h_user;
7799 if(handle->channels)
7801 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7802 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7804 while(handle->channels)
7805 del_channel_user(handle->channels, 1);
7810 handle_server_link(UNUSED_ARG(struct server *server))
7812 struct chanData *cData;
7814 for(cData = channelList; cData; cData = cData->next)
7816 if(!IsSuspended(cData))
7817 cData->may_opchan = 1;
7818 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7819 && !cData->channel->join_flooded
7820 && ((cData->channel->limit - cData->channel->members.used)
7821 < chanserv_conf.adjust_threshold))
7823 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7824 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7830 chanserv_conf_read(void)
7834 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7835 struct mod_chanmode *change;
7836 struct string_list *strlist;
7837 struct chanNode *chan;
7840 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7842 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7845 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7846 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7847 chanserv_conf.support_channels.used = 0;
7848 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7850 for(ii = 0; ii < strlist->used; ++ii)
7852 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7855 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7857 channelList_append(&chanserv_conf.support_channels, chan);
7860 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7863 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7866 chan = AddChannel(str, now, str2, NULL);
7868 channelList_append(&chanserv_conf.support_channels, chan);
7870 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7871 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7872 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7873 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7874 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7875 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7876 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7877 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7878 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7879 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7880 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7881 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7882 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7883 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7884 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7885 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7886 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7887 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7888 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7889 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7890 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7891 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7892 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7893 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7894 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7895 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7896 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7897 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7898 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7899 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7900 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7902 NickChange(chanserv, str, 0);
7903 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7904 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7905 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7906 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7907 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7908 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7909 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7910 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7911 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7912 chanserv_conf.max_owned = str ? atoi(str) : 5;
7913 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7914 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7915 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7916 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7917 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7918 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7919 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7920 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7921 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7922 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7923 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7924 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7925 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7928 safestrncpy(mode_line, str, sizeof(mode_line));
7929 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7930 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7931 && (change->argc < 2))
7933 chanserv_conf.default_modes = *change;
7934 mod_chanmode_free(change);
7936 free_string_list(chanserv_conf.set_shows);
7937 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7939 strlist = string_list_copy(strlist);
7942 static const char *list[] = {
7943 /* free form text */
7944 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7945 /* options based on user level */
7946 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7947 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7948 /* multiple choice options */
7949 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7950 /* binary options */
7951 "DynLimit", "NoDelete", "expire", "Vote",
7955 strlist = alloc_string_list(ArrayLength(list)-1);
7956 for(ii=0; list[ii]; ii++)
7957 string_list_append(strlist, strdup(list[ii]));
7959 chanserv_conf.set_shows = strlist;
7960 /* We don't look things up now, in case the list refers to options
7961 * defined by modules initialized after this point. Just mark the
7962 * function list as invalid, so it will be initialized.
7964 set_shows_list.used = 0;
7965 free_string_list(chanserv_conf.eightball);
7966 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7969 strlist = string_list_copy(strlist);
7973 strlist = alloc_string_list(4);
7974 string_list_append(strlist, strdup("Yes."));
7975 string_list_append(strlist, strdup("No."));
7976 string_list_append(strlist, strdup("Maybe so."));
7978 chanserv_conf.eightball = strlist;
7979 free_string_list(chanserv_conf.old_ban_names);
7980 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7982 strlist = string_list_copy(strlist);
7984 strlist = alloc_string_list(2);
7985 chanserv_conf.old_ban_names = strlist;
7986 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7987 off_channel = str ? atoi(str) : 0;
7989 str = database_get_data(conf_node, "oper_chan", RECDB_QSTRING);
7992 chanserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
7996 chanserv_conf.oper_channel = NULL;
8001 chanserv_note_type_read(const char *key, struct record_data *rd)
8004 struct note_type *ntype;
8007 if(!(obj = GET_RECORD_OBJECT(rd)))
8009 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8012 if(!(ntype = chanserv_create_note_type(key)))
8014 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8018 /* Figure out set access */
8019 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8021 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8022 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8024 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8026 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8027 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8029 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8031 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8035 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8036 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8037 ntype->set_access.min_opserv = 0;
8040 /* Figure out visibility */
8041 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8042 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8043 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8044 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8045 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8046 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8047 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8048 ntype->visible_type = NOTE_VIS_ALL;
8050 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8052 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8053 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8057 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8059 struct vote_option *vOpt;
8062 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8064 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8068 vOpt = calloc(1, sizeof(*vOpt));
8069 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8070 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8071 vOpt->voted = str ? atoi(str) : 0;
8072 vOpt->option_id = str ? atoi(key) : 0;
8073 vOpt->option_str = strdup(key);
8074 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8078 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8080 struct handle_info *handle;
8081 struct userData *uData;
8082 char *seen, *inf, *flags, *voted, *votefor, *expires;
8083 unsigned long last_seen;
8084 unsigned short access_level;
8086 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8088 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8092 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8093 if(access_level > UL_OWNER)
8095 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8099 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8100 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8101 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8102 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8103 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8104 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8105 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8106 handle = get_handle_info(key);
8109 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8113 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8114 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8115 uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8117 if((uData->flags & USER_SUSPENDED) && uData->expires)
8119 if(uData->expires > now)
8120 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8122 uData->flags &= ~USER_SUSPENDED;
8125 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8126 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8134 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8136 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8137 unsigned long set_time, triggered_time, expires_time;
8139 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8141 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8145 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8146 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8147 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8148 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8149 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8150 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8151 if (!reason || !owner)
8154 set_time = set ? strtoul(set, NULL, 0) : now;
8155 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8157 expires_time = strtoul(s_expires, NULL, 0);
8159 expires_time = set_time + atoi(s_duration);
8163 if(!reason || (expires_time && (expires_time < now)))
8166 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8169 static struct suspended *
8170 chanserv_read_suspended(dict_t obj)
8172 struct suspended *suspended = calloc(1, sizeof(*suspended));
8176 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8177 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8178 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8179 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8180 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8181 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8182 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8183 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8184 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8185 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8189 static struct giveownership *
8190 chanserv_read_giveownership(dict_t obj)
8192 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8196 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8197 giveownership->staff_issuer = str ? strdup(str) : NULL;
8199 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8201 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8202 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8204 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8205 giveownership->reason = str ? strdup(str) : NULL;
8206 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8207 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8209 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8210 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8211 return giveownership;
8215 chanserv_channel_read(const char *key, struct record_data *hir)
8217 struct suspended *suspended;
8218 struct giveownership *giveownership;
8219 struct mod_chanmode *modes;
8220 struct chanNode *cNode;
8221 struct chanData *cData;
8222 struct dict *channel, *obj;
8223 char *str, *argv[10];
8227 channel = hir->d.object;
8229 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8232 cNode = AddChannel(key, now, NULL, NULL);
8235 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8238 cData = register_channel(cNode, str);
8241 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8245 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8247 enum levelOption lvlOpt;
8248 enum charOption chOpt;
8250 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8251 cData->flags = atoi(str);
8253 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8255 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8257 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8258 else if(levelOptions[lvlOpt].old_flag)
8260 if(cData->flags & levelOptions[lvlOpt].old_flag)
8261 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8263 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8267 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8269 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8271 cData->chOpts[chOpt] = str[0];
8274 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8276 enum levelOption lvlOpt;
8277 enum charOption chOpt;
8280 cData->flags = base64toint(str, 5);
8281 count = strlen(str += 5);
8282 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8285 if(levelOptions[lvlOpt].old_flag)
8287 if(cData->flags & levelOptions[lvlOpt].old_flag)
8288 lvl = levelOptions[lvlOpt].flag_value;
8290 lvl = levelOptions[lvlOpt].default_value;
8292 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8294 case 'c': lvl = UL_COOWNER; break;
8295 case 'm': lvl = UL_MASTER; break;
8296 case 'n': lvl = UL_OWNER+1; break;
8297 case 'o': lvl = UL_OP; break;
8298 case 'p': lvl = UL_PEON; break;
8299 case 'w': lvl = UL_OWNER; break;
8300 default: lvl = 0; break;
8302 cData->lvlOpts[lvlOpt] = lvl;
8304 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8305 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8308 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8310 cData->expiry = atoi(str);
8311 if(cData->expiry > 0) {
8312 if(cData->expiry > now) {
8313 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8315 timeq_add(1, chanserv_expire_channel, cData);
8322 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8324 suspended = chanserv_read_suspended(obj);
8325 cData->suspended = suspended;
8326 suspended->cData = cData;
8327 /* We could use suspended->expires and suspended->revoked to
8328 * set the CHANNEL_SUSPENDED flag, but we don't. */
8330 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8332 suspended = calloc(1, sizeof(*suspended));
8333 suspended->issued = 0;
8334 suspended->revoked = 0;
8335 suspended->suspender = strdup(str);
8336 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8337 suspended->expires = str ? atoi(str) : 0;
8338 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8339 suspended->reason = strdup(str ? str : "No reason");
8340 suspended->previous = NULL;
8341 cData->suspended = suspended;
8342 suspended->cData = cData;
8346 cData->flags &= ~CHANNEL_SUSPENDED;
8347 suspended = NULL; /* to squelch a warning */
8350 if(IsSuspended(cData)) {
8351 if(suspended->expires > now)
8352 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8353 else if(suspended->expires)
8354 cData->flags &= ~CHANNEL_SUSPENDED;
8357 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8359 giveownership = chanserv_read_giveownership(obj);
8360 cData->giveownership = giveownership;
8363 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8364 struct mod_chanmode change;
8365 mod_chanmode_init(&change);
8367 change.args[0].mode = MODE_CHANOP;
8368 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8369 mod_chanmode_announce(chanserv, cNode, &change);
8372 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8373 cData->registered = str ? strtoul(str, NULL, 0) : now;
8374 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8375 cData->visited = str ? strtoul(str, NULL, 0) : now;
8376 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8377 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8378 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8379 cData->max = str ? atoi(str) : 0;
8380 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8381 cData->max_time = str ? atoi(str) : 0;
8382 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8383 cData->greeting = str ? strdup(str) : NULL;
8384 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8385 cData->user_greeting = str ? strdup(str) : NULL;
8386 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8387 cData->topic_mask = str ? strdup(str) : NULL;
8388 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8389 cData->topic = str ? strdup(str) : NULL;
8391 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8393 cData->vote = str ? strdup(str) : NULL;
8394 dict_delete(cData->vote_options);
8395 cData->vote_options = dict_new();
8396 dict_set_free_data(cData->vote_options, free_vote_options);
8397 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8398 cData->vote_start = str ? atoi(str) : 0;
8399 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8400 for(it = dict_first(obj); it; it = iter_next(it)) {
8401 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8405 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8406 for(it = dict_first(obj); it; it = iter_next(it))
8408 struct record_data *rd = iter_data(it);
8409 if(rd->type != RECDB_QSTRING) continue;
8410 int advtopic_index = atoi(iter_key(it));
8411 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8412 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8415 if(!IsSuspended(cData)
8416 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8417 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8418 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8419 cData->modes = *modes;
8421 cData->modes.modes_set |= MODE_REGISTERED;
8422 if(cData->modes.argc > 1)
8423 cData->modes.argc = 1;
8424 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8425 mod_chanmode_free(modes);
8428 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8429 for(it = dict_first(obj); it; it = iter_next(it))
8430 user_read_helper(iter_key(it), iter_data(it), cData);
8432 if(!cData->users && !IsProtected(cData))
8434 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8435 unregister_channel(cData, "has empty user list.");
8439 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8440 for(it = dict_first(obj); it; it = iter_next(it))
8441 ban_read_helper(iter_key(it), iter_data(it), cData);
8443 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8444 for(it = dict_first(obj); it; it = iter_next(it))
8446 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8447 struct record_data *rd = iter_data(it);
8448 const char *note, *setter;
8450 if(rd->type != RECDB_OBJECT)
8452 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8456 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8458 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8460 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8464 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8465 if(!setter) setter = "<unknown>";
8466 chanserv_add_channel_note(cData, ntype, setter, note);
8474 chanserv_dnr_read(const char *key, struct record_data *hir)
8476 const char *setter, *reason, *str;
8477 struct do_not_register *dnr;
8478 unsigned long expiry;
8480 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8483 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8486 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8489 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8492 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8493 expiry = str ? strtoul(str, NULL, 0) : 0;
8494 if(expiry && expiry <= now)
8496 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8499 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8501 dnr->set = atoi(str);
8507 chanserv_saxdb_read(struct dict *database)
8509 struct dict *section;
8512 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8513 for(it = dict_first(section); it; it = iter_next(it))
8514 chanserv_note_type_read(iter_key(it), iter_data(it));
8516 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8517 for(it = dict_first(section); it; it = iter_next(it))
8518 chanserv_channel_read(iter_key(it), iter_data(it));
8520 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8521 for(it = dict_first(section); it; it = iter_next(it))
8522 chanserv_dnr_read(iter_key(it), iter_data(it));
8528 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8530 int high_present = 0;
8531 saxdb_start_record(ctx, KEY_USERS, 1);
8532 for(; uData; uData = uData->next)
8534 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8536 saxdb_start_record(ctx, uData->handle->handle, 0);
8537 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8538 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8540 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8542 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8543 if(uData->channel->vote && uData->voted)
8544 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8545 if(uData->channel->vote && uData->votefor)
8546 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8548 saxdb_write_string(ctx, KEY_INFO, uData->info);
8549 saxdb_end_record(ctx);
8551 saxdb_end_record(ctx);
8552 return high_present;
8556 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8560 saxdb_start_record(ctx, KEY_BANS, 1);
8561 for(; bData; bData = bData->next)
8563 saxdb_start_record(ctx, bData->mask, 0);
8564 saxdb_write_int(ctx, KEY_SET, bData->set);
8565 if(bData->triggered)
8566 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8568 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8570 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8572 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8573 saxdb_end_record(ctx);
8575 saxdb_end_record(ctx);
8579 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8581 saxdb_start_record(ctx, name, 0);
8582 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8583 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8585 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8587 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8589 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8591 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8592 saxdb_end_record(ctx);
8596 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8598 saxdb_start_record(ctx, name, 0);
8599 if(giveownership->staff_issuer)
8600 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8601 if(giveownership->old_owner)
8602 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8603 if(giveownership->target)
8604 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8605 if(giveownership->target_access)
8606 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8607 if(giveownership->reason)
8608 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8609 if(giveownership->issued)
8610 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8611 if(giveownership->previous)
8612 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8613 saxdb_end_record(ctx);
8617 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8621 enum levelOption lvlOpt;
8622 enum charOption chOpt;
8625 saxdb_start_record(ctx, channel->channel->name, 1);
8627 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8628 saxdb_write_int(ctx, KEY_MAX, channel->max);
8629 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8631 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8632 if(channel->registrar)
8633 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8634 if(channel->greeting)
8635 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8636 if(channel->user_greeting)
8637 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8638 if(channel->topic_mask)
8639 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8640 if(channel->suspended)
8641 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8642 if(channel->giveownership)
8643 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8645 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8648 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8649 if(channel->vote_start)
8650 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8651 if (dict_size(channel->vote_options)) {
8652 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8653 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8654 struct vote_option *vOpt = iter_data(it);
8656 sprintf(str,"%i",vOpt->option_id);
8657 saxdb_start_record(ctx, str, 0);
8659 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8661 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8662 saxdb_end_record(ctx);
8664 saxdb_end_record(ctx);
8668 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8669 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8670 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8671 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8672 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8674 buf[0] = channel->chOpts[chOpt];
8676 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8678 saxdb_end_record(ctx);
8680 if(channel->modes.modes_set || channel->modes.modes_clear)
8682 mod_chanmode_format(&channel->modes, buf);
8683 saxdb_write_string(ctx, KEY_MODES, buf);
8686 high_present = chanserv_write_users(ctx, channel->users);
8687 chanserv_write_bans(ctx, channel->bans);
8689 if(channel->flags & CHANNEL_ADVTOPIC) {
8690 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8692 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8693 if(channel->advtopic[advtopic_index])
8694 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8696 saxdb_end_record(ctx);
8699 if(dict_size(channel->notes))
8703 saxdb_start_record(ctx, KEY_NOTES, 1);
8704 for(it = dict_first(channel->notes); it; it = iter_next(it))
8706 struct note *note = iter_data(it);
8707 saxdb_start_record(ctx, iter_key(it), 0);
8708 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8709 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8710 saxdb_end_record(ctx);
8712 saxdb_end_record(ctx);
8715 if(channel->ownerTransfer)
8716 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8717 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8718 saxdb_end_record(ctx);
8722 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8726 saxdb_start_record(ctx, ntype->name, 0);
8727 switch(ntype->set_access_type)
8729 case NOTE_SET_CHANNEL_ACCESS:
8730 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8732 case NOTE_SET_CHANNEL_SETTER:
8733 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8735 case NOTE_SET_PRIVILEGED: default:
8736 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8739 switch(ntype->visible_type)
8741 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8742 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8743 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8745 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8746 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8747 saxdb_end_record(ctx);
8751 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8753 struct do_not_register *dnr;
8754 dict_iterator_t it, next;
8756 for(it = dict_first(dnrs); it; it = next)
8758 next = iter_next(it);
8759 dnr = iter_data(it);
8760 if(dnr->expires && dnr->expires <= now)
8762 dict_remove(dnrs, iter_key(it));
8765 saxdb_start_record(ctx, dnr->chan_name, 0);
8767 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8769 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8770 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8771 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8772 saxdb_end_record(ctx);
8777 chanserv_saxdb_write(struct saxdb_context *ctx)
8780 struct chanData *channel;
8783 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8784 for(it = dict_first(note_types); it; it = iter_next(it))
8785 chanserv_write_note_type(ctx, iter_data(it));
8786 saxdb_end_record(ctx);
8789 saxdb_start_record(ctx, KEY_DNR, 1);
8790 write_dnrs_helper(ctx, handle_dnrs);
8791 write_dnrs_helper(ctx, plain_dnrs);
8792 write_dnrs_helper(ctx, mask_dnrs);
8793 saxdb_end_record(ctx);
8796 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8797 for(channel = channelList; channel; channel = channel->next)
8798 chanserv_write_channel(ctx, channel);
8799 saxdb_end_record(ctx);
8805 chanserv_db_cleanup(void) {
8807 unreg_part_func(handle_part);
8809 unregister_channel(channelList, "terminating.");
8810 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8811 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8812 free(chanserv_conf.support_channels.list);
8813 dict_delete(handle_dnrs);
8814 dict_delete(plain_dnrs);
8815 dict_delete(mask_dnrs);
8816 dict_delete(note_types);
8817 free_string_list(chanserv_conf.eightball);
8818 free_string_list(chanserv_conf.old_ban_names);
8819 free_string_list(chanserv_conf.set_shows);
8820 free(set_shows_list.list);
8821 free(uset_shows_list.list);
8824 struct userData *helper = helperList;
8825 helperList = helperList->next;
8830 #if defined(GCC_VARMACROS)
8831 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8832 #elif defined(C99_VARMACROS)
8833 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8835 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8836 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8839 init_chanserv(const char *nick)
8841 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8842 conf_register_reload(chanserv_conf_read);
8846 reg_server_link_func(handle_server_link);
8847 reg_new_channel_func(handle_new_channel);
8848 reg_join_func(handle_join);
8849 reg_part_func(handle_part);
8850 reg_kick_func(handle_kick);
8851 reg_topic_func(handle_topic);
8852 reg_mode_change_func(handle_mode);
8853 reg_nick_change_func(handle_nick_change);
8854 reg_auth_func(handle_auth);
8857 reg_handle_rename_func(handle_rename);
8858 reg_unreg_func(handle_unreg);
8860 handle_dnrs = dict_new();
8861 dict_set_free_data(handle_dnrs, free);
8862 plain_dnrs = dict_new();
8863 dict_set_free_data(plain_dnrs, free);
8864 mask_dnrs = dict_new();
8865 dict_set_free_data(mask_dnrs, free);
8867 reg_svccmd_unbind_func(handle_svccmd_unbind);
8868 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8869 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8870 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8871 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8872 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8873 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8874 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8875 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8876 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8877 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8878 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8879 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8880 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8882 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8883 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8885 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8886 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8887 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8888 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8889 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8891 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8892 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8893 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8894 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8895 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8897 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8898 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8899 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8900 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8902 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8903 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8904 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8905 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8906 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8907 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8908 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8909 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8911 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8912 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8913 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8914 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8915 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8916 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8917 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8918 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8919 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8920 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8921 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8922 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8923 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8924 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8925 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8927 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8928 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8929 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8930 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8931 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8933 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8934 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8936 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8937 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8938 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8939 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8940 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8941 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8942 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8943 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8944 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8945 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8946 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8948 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8949 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8951 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8952 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8953 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8954 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8956 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8957 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8958 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8959 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8960 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8962 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8963 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8964 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8965 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8966 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8967 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8969 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8970 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8971 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8972 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8973 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8974 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8975 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8976 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8978 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8980 /* Channel options */
8981 DEFINE_CHANNEL_OPTION(defaulttopic);
8982 DEFINE_CHANNEL_OPTION(topicmask);
8983 DEFINE_CHANNEL_OPTION(greeting);
8984 DEFINE_CHANNEL_OPTION(usergreeting);
8985 DEFINE_CHANNEL_OPTION(modes);
8986 DEFINE_CHANNEL_OPTION(enfops);
8987 DEFINE_CHANNEL_OPTION(giveops);
8988 DEFINE_CHANNEL_OPTION(protect);
8989 DEFINE_CHANNEL_OPTION(enfmodes);
8990 DEFINE_CHANNEL_OPTION(enftopic);
8991 DEFINE_CHANNEL_OPTION(pubcmd);
8992 DEFINE_CHANNEL_OPTION(givevoice);
8993 DEFINE_CHANNEL_OPTION(userinfo);
8994 DEFINE_CHANNEL_OPTION(dynlimit);
8995 DEFINE_CHANNEL_OPTION(topicsnarf);
8996 DEFINE_CHANNEL_OPTION(vote);
8997 DEFINE_CHANNEL_OPTION(nodelete);
8998 DEFINE_CHANNEL_OPTION(toys);
8999 DEFINE_CHANNEL_OPTION(setters);
9000 DEFINE_CHANNEL_OPTION(topicrefresh);
9001 DEFINE_CHANNEL_OPTION(ctcpusers);
9002 DEFINE_CHANNEL_OPTION(ctcpreaction);
9003 DEFINE_CHANNEL_OPTION(inviteme);
9004 DEFINE_CHANNEL_OPTION(advtopic);
9005 DEFINE_CHANNEL_OPTION(unreviewed);
9006 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
9007 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9008 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9010 DEFINE_CHANNEL_OPTION(offchannel);
9011 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9013 /* Alias set topic to set defaulttopic for compatibility. */
9014 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9017 DEFINE_USER_OPTION(noautoop);
9018 DEFINE_USER_OPTION(autoinvite);
9019 DEFINE_USER_OPTION(info);
9021 /* Alias uset autovoice to uset autoop. */
9022 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9024 note_types = dict_new();
9025 dict_set_free_data(note_types, chanserv_deref_note_type);
9028 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9029 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9030 service_register(chanserv)->trigger = '!';
9031 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9033 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9035 if(chanserv_conf.channel_expire_frequency)
9036 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9038 if(chanserv_conf.dnr_expire_frequency)
9039 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9041 if(chanserv_conf.refresh_period)
9043 unsigned long next_refresh;
9044 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9045 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9048 reg_exit_func(chanserv_db_cleanup);
9049 message_register_table(msgtab);