1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() and devnull management */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_MIN_TIME_BANS "min_time_bans"
47 #define KEY_NICK "nick"
48 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
49 #define KEY_8BALL_RESPONSES "8ball"
50 #define KEY_OLD_BAN_NAMES "old_ban_names"
51 #define KEY_REFRESH_PERIOD "refresh_period"
52 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
53 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
54 #define KEY_MAX_OWNED "max_owned"
55 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
56 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
57 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
58 #define KEY_NODELETE_LEVEL "nodelete_level"
59 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
60 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
61 #define KEY_INVITED_INTERVAL "invite_timeout"
62 #define KEY_REVOKE_MODE_A "revoke_mode_a"
63 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
64 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
65 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
67 /* ChanServ database */
68 #define KEY_CHANNELS "channels"
69 #define KEY_NOTE_TYPES "note_types"
71 /* Note type parameters */
72 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
73 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
74 #define KEY_NOTE_SETTER_ACCESS "setter_access"
75 #define KEY_NOTE_VISIBILITY "visibility"
76 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
77 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
78 #define KEY_NOTE_VIS_ALL "all"
79 #define KEY_NOTE_MAX_LENGTH "max_length"
80 #define KEY_NOTE_SETTER "setter"
81 #define KEY_NOTE_NOTE "note"
83 /* Do-not-register channels */
85 #define KEY_DNR_SET "set"
86 #define KEY_DNR_SETTER "setter"
87 #define KEY_DNR_REASON "reason"
90 #define KEY_REGISTERED "registered"
91 #define KEY_REGISTRAR "registrar"
92 #define KEY_SUSPENDED "suspended"
93 #define KEY_PREVIOUS "previous"
94 #define KEY_SUSPENDER "suspender"
95 #define KEY_ISSUED "issued"
96 #define KEY_REVOKED "revoked"
97 #define KEY_SUSPEND_EXPIRES "suspend_expires"
98 #define KEY_SUSPEND_REASON "suspend_reason"
99 #define KEY_GIVEOWNERSHIP "giveownership"
100 #define KEY_STAFF_ISSUER "staff_issuer"
101 #define KEY_OLD_OWNER "old_owner"
102 #define KEY_TARGET "target"
103 #define KEY_TARGET_ACCESS "target_access"
104 #define KEY_VISITED "visited"
105 #define KEY_TOPIC "topic"
106 #define KEY_GREETING "greeting"
107 #define KEY_USER_GREETING "user_greeting"
108 #define KEY_MODES "modes"
109 #define KEY_FLAGS "flags"
110 #define KEY_OPTIONS "options"
111 #define KEY_USERS "users"
112 #define KEY_BANS "bans"
113 #define KEY_MAX "max"
114 #define KEY_MAX_TIME "max_time"
115 #define KEY_NOTES "notes"
116 #define KEY_TOPIC_MASK "topic_mask"
117 #define KEY_ADVTOPIC_ENTRIES "adv_topic"
118 #define KEY_OWNER_TRANSFER "owner_transfer"
119 #define KEY_EXPIRE "expire"
122 #define KEY_LEVEL "level"
123 #define KEY_INFO "info"
124 #define KEY_SEEN "seen"
127 #define KEY_VOTE "vote"
128 #define KEY_VOTE_START "votestart"
129 #define KEY_VOTE_OPTIONS "voptions"
130 #define KEY_VOTE_OPTION_NAME "voptionname"
131 #define KEY_VOTE_VOTED "vvoted"
132 #define KEY_VOTE_VOTEDFOR "vvotefor"
133 #define KEY_VOTE_OPTION_ID "voptionid"
134 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
137 #define KEY_OWNER "owner"
138 #define KEY_REASON "reason"
139 #define KEY_SET "set"
140 #define KEY_DURATION "duration"
141 #define KEY_EXPIRES "expires"
142 #define KEY_TRIGGERED "triggered"
144 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_UNREVIEWED)
145 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
146 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
148 /* Administrative messages */
149 static const struct message_entry msgtab[] = {
150 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
152 /* Channel registration */
153 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
154 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
155 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
156 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
157 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
158 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
160 /* Do-not-register channels */
161 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
162 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
163 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
164 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
165 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
166 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
167 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
168 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
169 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
170 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
171 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
172 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
173 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
174 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
176 /* Channel unregistration */
177 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
178 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
179 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
180 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
183 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
184 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
186 /* Channel merging */
187 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
188 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
189 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
190 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
191 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
193 /* Handle unregistration */
194 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
197 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
198 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
199 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
200 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
201 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
202 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
203 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
204 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
205 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
206 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
207 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
208 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
209 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
210 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
212 /* Removing yourself from a channel. */
213 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
214 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
215 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
217 /* User management */
218 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
219 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
220 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
221 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
222 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
223 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
224 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
225 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
227 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
228 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
229 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
230 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
231 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
232 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
233 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
236 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
237 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
238 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
239 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
240 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
241 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
242 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
243 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
244 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
245 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
246 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
247 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
248 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
249 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
250 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
251 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
253 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
255 /* Channel management */
256 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
257 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
258 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
260 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
261 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
262 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
263 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
264 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
265 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
266 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
267 { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
269 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
270 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
271 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
272 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
273 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
274 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
275 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
276 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
277 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
278 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
279 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
280 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
281 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
282 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
283 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
284 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
285 { "CSMSG_SET_ADVTOPIC", "$bAdvTopic $b %s" },
286 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
287 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
288 { "CSMSG_SET_MODES", "$bModes $b %s" },
289 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
290 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
291 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
292 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
293 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
294 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
295 { "CSMSG_SET_VOTE", "$bVote $b %d" },
296 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
297 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
298 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
299 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
300 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
301 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
302 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
303 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
304 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
305 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
306 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
307 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
308 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
309 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
310 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
311 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
312 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
313 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
314 { "CSMSG_USET_INFO", "$bInfo $b %s" },
316 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
317 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
318 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
319 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
320 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
321 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
322 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
323 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
324 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
325 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
326 { "CSMSG_PROTECT_NONE", "No users will be protected." },
327 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
328 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
329 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
330 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
331 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
332 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
333 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
334 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
335 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
336 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
337 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
338 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
340 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
341 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
342 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
343 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
344 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
345 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
346 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
347 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
349 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
350 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
351 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
353 /* Channel userlist */
354 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
355 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
356 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
357 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
358 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
360 /* Channel note list */
361 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
362 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
363 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
364 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
365 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
366 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
367 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
368 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
369 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
370 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
371 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
372 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
373 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
374 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
375 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
377 /* Channel [un]suspension */
378 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
379 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
380 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
381 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
382 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
383 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
384 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
386 /* Access information */
387 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
388 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
389 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
390 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
391 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
392 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
393 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
394 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
395 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
396 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
397 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
398 { "CSMSG_UC_H_TITLE", "network helper" },
399 { "CSMSG_LC_H_TITLE", "support helper" },
400 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
401 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
402 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
405 /* Seen information */
406 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
407 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
408 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
409 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
411 /* Names information */
412 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
413 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
415 /* Channel information */
416 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
417 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
418 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
419 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
420 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
421 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
422 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
423 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
424 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
425 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
426 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
427 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
428 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
435 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
436 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
437 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
439 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
440 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
441 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
442 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
444 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
445 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
446 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
447 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
448 { "CSMSG_PEEK_OPS", "$bOps:$b" },
449 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
451 /* Network information */
452 { "CSMSG_NETWORK_INFO", "Network Information:" },
453 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
454 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
455 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
456 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
457 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
458 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
459 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
460 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
463 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
464 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
465 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
467 /* Channel searches */
468 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
469 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
470 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
471 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
473 /* Channel configuration */
474 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
475 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
476 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
477 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
480 { "CSMSG_USER_OPTIONS", "User Options:" },
481 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
484 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
485 { "CSMSG_PING_RESPONSE", "Pong!" },
486 { "CSMSG_WUT_RESPONSE", "wut" },
487 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
488 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
489 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
490 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
491 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
492 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
493 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
496 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
497 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
498 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
499 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
500 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
501 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
502 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
503 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
504 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
505 { "CSMSG_VOTE_QUESTION", "Question: %s" },
506 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
507 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
508 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
509 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
510 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
511 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
512 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
513 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
514 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
515 { "CSMSG_VOTE_VOTED", "You have already voted." },
516 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
517 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
518 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
519 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
522 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
526 #define CSMSG_ALERT_REGISTERED "%s registered to %s by %s."
527 #define CSMSG_ALERT_UNREGISTERED "%s %s"
529 /* eject_user and unban_user flags */
530 #define ACTION_KICK 0x0001
531 #define ACTION_BAN 0x0002
532 #define ACTION_ADD_BAN 0x0004
533 #define ACTION_ADD_TIMED_BAN 0x0008
534 #define ACTION_UNBAN 0x0010
535 #define ACTION_DEL_BAN 0x0020
537 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
538 #define MODELEN 40 + KEYLEN
542 #define CSFUNC_ARGS user, channel, argc, argv, cmd
544 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
545 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
546 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
547 reply("MSG_MISSING_PARAMS", argv[0]); \
551 DECLARE_LIST(dnrList, struct do_not_register *);
552 DEFINE_LIST(dnrList, struct do_not_register *)
554 #define chanserv_notice(target, format...) send_message(target , chanserv , ## format)
555 #define chanserv_oper_message(format...) do { if(chanserv_conf.oper_channel) send_channel_message(chanserv_conf.oper_channel , chanserv , ## format); } while(0)
557 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
559 struct userNode *chanserv;
562 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
563 static struct log_type *CS_LOG;
567 struct channelList support_channels;
568 struct mod_chanmode default_modes;
570 unsigned long db_backup_frequency;
571 unsigned long channel_expire_frequency;
572 unsigned long dnr_expire_frequency;
574 unsigned long invited_timeout;
576 unsigned long info_delay;
577 unsigned long adjust_delay;
578 unsigned long channel_expire_delay;
579 unsigned int nodelete_level;
581 unsigned int adjust_threshold;
582 int join_flood_threshold;
584 unsigned int greeting_length;
585 unsigned int refresh_period;
586 unsigned int giveownership_period;
588 unsigned int max_owned;
589 unsigned int max_chan_users;
590 unsigned int max_chan_bans;
591 unsigned int min_time_bans;
592 unsigned int max_userinfo_length;
594 unsigned int revoke_mode_a;
596 struct string_list *set_shows;
597 struct string_list *eightball;
598 struct string_list *old_ban_names;
600 const char *ctcp_short_ban_duration;
601 const char *ctcp_long_ban_duration;
603 const char *irc_operator_epithet;
604 const char *network_helper_epithet;
605 const char *support_helper_epithet;
607 const char *new_channel_authed;
608 const char *new_channel_unauthed;
609 const char *new_channel_msg;
611 struct chanNode *oper_channel;
616 struct userNode *user;
617 struct userNode *bot;
618 struct chanNode *channel;
620 unsigned short lowest;
621 unsigned short highest;
622 struct userData **users;
623 struct helpfile_table table;
628 struct userNode *user;
629 struct chanNode *chan;
632 enum note_access_type
634 NOTE_SET_CHANNEL_ACCESS,
635 NOTE_SET_CHANNEL_SETTER,
639 enum note_visible_type
642 NOTE_VIS_CHANNEL_USERS,
648 enum note_access_type set_access_type;
650 unsigned int min_opserv;
651 unsigned short min_ulevel;
653 enum note_visible_type visible_type;
654 unsigned int max_length;
661 struct note_type *type;
662 char setter[NICKSERV_HANDLE_LEN+1];
666 static unsigned int registered_channels;
667 static unsigned int banCount;
669 static const struct {
672 unsigned short level;
675 { "peon", "Peon", UL_PEON, '+' },
676 { "op", "Op", UL_OP, '@' },
677 { "master", "Master", UL_MASTER, '%' },
678 { "coowner", "Coowner", UL_COOWNER, '*' },
679 { "owner", "Owner", UL_OWNER, '!' },
680 { "helper", "BUG:", UL_HELPER, 'X' }
683 static const struct {
686 unsigned short default_value;
687 unsigned int old_idx;
688 unsigned int old_flag;
689 unsigned short flag_value;
691 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
692 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
693 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
694 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
695 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
696 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
697 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
698 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
699 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
700 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
701 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
702 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
705 struct charOptionValues {
708 } protectValues[] = {
709 { 'a', "CSMSG_PROTECT_ALL" },
710 { 'e', "CSMSG_PROTECT_EQUAL" },
711 { 'l', "CSMSG_PROTECT_LOWER" },
712 { 'n', "CSMSG_PROTECT_NONE" }
714 { 'd', "CSMSG_TOYS_DISABLED" },
715 { 'n', "CSMSG_TOYS_PRIVATE" },
716 { 'p', "CSMSG_TOYS_PUBLIC" }
717 }, topicRefreshValues[] = {
718 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
719 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
720 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
721 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
722 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
723 }, ctcpReactionValues[] = {
724 { 'k', "CSMSG_CTCPREACTION_KICK" },
725 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
726 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
727 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
730 static const struct {
734 unsigned int old_idx;
736 struct charOptionValues *values;
738 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
739 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
740 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
741 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
744 struct userData *helperList;
745 struct chanData *channelList;
746 static struct module *chanserv_module;
747 static unsigned int userCount;
749 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
750 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
751 static void unregister_channel(struct chanData *channel, const char *reason);
754 user_level_from_name(const char *name, unsigned short clamp_level)
756 unsigned int level = 0, ii;
758 level = strtoul(name, NULL, 10);
759 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
760 if(!irccasecmp(name, accessLevels[ii].name))
761 level = accessLevels[ii].level;
762 if(level > clamp_level)
768 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
771 *minl = strtoul(arg, &sep, 10);
779 *maxl = strtoul(sep+1, &sep, 10);
787 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
789 struct userData *uData, **head;
791 if(!channel || !handle)
794 if(override && HANDLE_FLAGGED(handle, HELPING)
795 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
797 for(uData = helperList;
798 uData && uData->handle != handle;
799 uData = uData->next);
803 uData = calloc(1, sizeof(struct userData));
804 uData->handle = handle;
806 uData->access = UL_HELPER;
812 uData->next = helperList;
814 helperList->prev = uData;
822 for(uData = channel->users; uData; uData = uData->next)
823 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
826 head = &(channel->users);
829 if(uData && (uData != *head))
831 /* Shuffle the user to the head of whatever list he was in. */
833 uData->next->prev = uData->prev;
835 uData->prev->next = uData->next;
841 (**head).prev = uData;
848 /* Returns non-zero if user has at least the minimum access.
849 * exempt_owner is set when handling !set, so the owner can set things
852 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
854 struct userData *uData;
855 struct chanData *cData = channel->channel_info;
856 unsigned short minimum = cData->lvlOpts[opt];
859 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
862 if(minimum <= uData->access)
864 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
869 /* Scan for other users authenticated to the same handle
870 still in the channel. If so, keep them listed as present.
872 user is optional, if not null, it skips checking that userNode
873 (for the handle_part function) */
875 scan_user_presence(struct userData *uData, struct userNode *user)
879 if(IsSuspended(uData->channel)
880 || IsUserSuspended(uData)
881 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
893 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
895 unsigned int eflags, argc;
897 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
899 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
900 if(!channel->channel_info
901 || IsSuspended(channel->channel_info)
903 || !ircncasecmp(text, "ACTION ", 7))
905 /* Figure out the minimum level needed to CTCP the channel */
906 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
908 /* We need to enforce against them; do so. */
910 argv[0] = (char*)text;
911 argv[1] = user->nick;
913 if(GetUserMode(channel, user))
914 eflags |= ACTION_KICK;
915 switch(channel->channel_info->chOpts[chCTCPReaction]) {
916 default: case 'k': /* just do the kick */ break;
918 eflags |= ACTION_BAN;
921 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
922 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
925 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
926 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
929 argv[argc++] = bad_ctcp_reason;
930 eject_user(chanserv, channel, argc, argv, NULL, eflags);
934 chanserv_create_note_type(const char *name)
936 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
937 strcpy(ntype->name, name);
939 dict_insert(note_types, ntype->name, ntype);
944 free_vote_options(void *data)
946 struct vote_option *vOpt = data;
948 free(vOpt->option_str);
953 chanserv_deref_note_type(void *data)
955 struct note_type *ntype = data;
957 if(--ntype->refs > 0)
963 chanserv_flush_note_type(struct note_type *ntype)
965 struct chanData *cData;
966 for(cData = channelList; cData; cData = cData->next)
967 dict_remove(cData->notes, ntype->name);
971 chanserv_truncate_notes(struct note_type *ntype)
973 struct chanData *cData;
975 unsigned int size = sizeof(*note) + ntype->max_length;
977 for(cData = channelList; cData; cData = cData->next) {
978 note = dict_find(cData->notes, ntype->name, NULL);
981 if(strlen(note->note) <= ntype->max_length)
983 dict_remove2(cData->notes, ntype->name, 1);
984 note = realloc(note, size);
985 note->note[ntype->max_length] = 0;
986 dict_insert(cData->notes, ntype->name, note);
990 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
993 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
996 unsigned int len = strlen(text);
998 if(len > type->max_length) len = type->max_length;
999 note = calloc(1, sizeof(*note) + len);
1001 strncpy(note->setter, setter, sizeof(note->setter)-1);
1002 memcpy(note->note, text, len);
1003 note->note[len] = 0;
1004 dict_insert(channel->notes, type->name, note);
1010 chanserv_free_note(void *data)
1012 struct note *note = data;
1014 chanserv_deref_note_type(note->type);
1015 assert(note->type->refs > 0); /* must use delnote to remove the type */
1019 static MODCMD_FUNC(cmd_createnote) {
1020 struct note_type *ntype;
1021 unsigned int arg = 1, existed = 0, max_length;
1023 if((ntype = dict_find(note_types, argv[1], NULL)))
1026 ntype = chanserv_create_note_type(argv[arg]);
1027 if(!irccasecmp(argv[++arg], "privileged"))
1030 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1031 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1033 else if(!irccasecmp(argv[arg], "channel"))
1035 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1038 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1041 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1042 ntype->set_access.min_ulevel = ulvl;
1044 else if(!irccasecmp(argv[arg], "setter"))
1046 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1050 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1054 if(!irccasecmp(argv[++arg], "privileged"))
1055 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1056 else if(!irccasecmp(argv[arg], "channel_users"))
1057 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1058 else if(!irccasecmp(argv[arg], "all"))
1059 ntype->visible_type = NOTE_VIS_ALL;
1061 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1065 if((arg+1) >= argc) {
1066 reply("MSG_MISSING_PARAMS", argv[0]);
1069 max_length = strtoul(argv[++arg], NULL, 0);
1070 if(max_length < 20 || max_length > 450)
1072 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1075 if(existed && (max_length < ntype->max_length))
1077 ntype->max_length = max_length;
1078 chanserv_truncate_notes(ntype);
1080 ntype->max_length = max_length;
1083 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1085 reply("CSMSG_NOTE_CREATED", ntype->name);
1090 dict_remove(note_types, ntype->name);
1094 static MODCMD_FUNC(cmd_removenote) {
1095 struct note_type *ntype;
1098 ntype = dict_find(note_types, argv[1], NULL);
1099 force = (argc > 2) && !irccasecmp(argv[2], "force");
1102 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1109 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1112 chanserv_flush_note_type(ntype);
1114 dict_remove(note_types, argv[1]);
1115 reply("CSMSG_NOTE_DELETED", argv[1]);
1120 chanserv_expire_channel(void *data)
1122 struct chanData *channel = data;
1123 char reason[MAXLEN];
1124 sprintf(reason, "channel expired.");
1125 channel->expiry = 0;
1126 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1127 unregister_channel(channel, reason);
1130 static MODCMD_FUNC(chan_opt_expire)
1132 struct chanData *cData = channel->channel_info;
1133 unsigned long value = cData->expiry;
1137 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1139 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1142 unsigned long expiry,duration;
1144 /* The two directions can have different ACLs. */
1145 if(!strcmp(argv[1], "0"))
1147 else if((duration = ParseInterval(argv[1])))
1148 expiry = now + duration;
1151 reply("MSG_INVALID_DURATION", argv[1]);
1155 if (expiry != value)
1159 timeq_del(value, chanserv_expire_channel, cData, 0);
1162 cData->expiry = value;
1165 timeq_add(expiry, chanserv_expire_channel, cData);
1170 if(cData->expiry > now) {
1171 char expirestr[INTERVALLEN];
1172 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1174 reply("CSMSG_SET_EXPIRE_OFF");
1179 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1183 if(orig->modes_set & change->modes_clear)
1185 if(orig->modes_clear & change->modes_set)
1187 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1188 && strcmp(orig->new_key, change->new_key))
1190 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1191 && (orig->new_limit != change->new_limit))
1196 static char max_length_text[MAXLEN+1][16];
1198 static struct helpfile_expansion
1199 chanserv_expand_variable(const char *variable)
1201 struct helpfile_expansion exp;
1203 if(!irccasecmp(variable, "notes"))
1206 exp.type = HF_TABLE;
1207 exp.value.table.length = 1;
1208 exp.value.table.width = 3;
1209 exp.value.table.flags = 0;
1210 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1211 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1212 exp.value.table.contents[0][0] = "Note Type";
1213 exp.value.table.contents[0][1] = "Visibility";
1214 exp.value.table.contents[0][2] = "Max Length";
1215 for(it=dict_first(note_types); it; it=iter_next(it))
1217 struct note_type *ntype = iter_data(it);
1220 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1221 row = exp.value.table.length++;
1222 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1223 exp.value.table.contents[row][0] = ntype->name;
1224 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1225 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1227 if(!max_length_text[ntype->max_length][0])
1228 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1229 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1234 exp.type = HF_STRING;
1235 exp.value.str = NULL;
1239 static struct chanData*
1240 register_channel(struct chanNode *cNode, char *registrar)
1242 struct chanData *channel;
1243 enum levelOption lvlOpt;
1244 enum charOption chOpt;
1247 channel = calloc(1, sizeof(struct chanData));
1249 channel->notes = dict_new();
1250 dict_set_free_data(channel->notes, chanserv_free_note);
1252 channel->registrar = strdup(registrar);
1253 channel->registered = now;
1254 channel->visited = now;
1255 channel->limitAdjusted = now;
1256 channel->ownerTransfer = now;
1257 channel->flags = CHANNEL_DEFAULT_FLAGS;
1258 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1259 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1260 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1261 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1262 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1263 channel->advtopic[i] = NULL;
1265 channel->prev = NULL;
1266 channel->next = channelList;
1269 channelList->prev = channel;
1270 channelList = channel;
1271 registered_channels++;
1273 channel->channel = cNode;
1275 cNode->channel_info = channel;
1277 channel->vote = NULL;
1282 static struct userData*
1283 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1285 struct userData *ud;
1287 if(access_level > UL_OWNER)
1290 ud = calloc(1, sizeof(*ud));
1291 ud->channel = channel;
1292 ud->handle = handle;
1294 ud->access = access_level;
1295 ud->info = info ? strdup(info) : NULL;
1298 ud->next = channel->users;
1300 channel->users->prev = ud;
1301 channel->users = ud;
1303 channel->userCount++;
1307 ud->u_next = ud->handle->channels;
1309 ud->u_next->u_prev = ud;
1310 ud->handle->channels = ud;
1316 del_channel_user(struct userData *user, int do_gc)
1318 struct chanData *channel = user->channel;
1320 channel->userCount--;
1324 user->prev->next = user->next;
1326 channel->users = user->next;
1328 user->next->prev = user->prev;
1331 user->u_prev->u_next = user->u_next;
1333 user->handle->channels = user->u_next;
1335 user->u_next->u_prev = user->u_prev;
1339 if(do_gc && !channel->users && !IsProtected(channel)) {
1340 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1341 unregister_channel(channel, "lost all users.");
1345 static void expire_ban(void *data);
1348 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1351 unsigned int ii, l1, l2;
1356 bd = malloc(sizeof(struct banData));
1358 bd->channel = channel;
1360 bd->triggered = triggered;
1361 bd->expires = expires;
1363 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1365 extern const char *hidden_host_suffix;
1366 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1370 l2 = strlen(old_name);
1373 if(irccasecmp(mask + l1 - l2, old_name))
1375 new_mask = alloca(MAXLEN);
1376 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1379 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1381 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1382 bd->reason = strdup(reason);
1385 timeq_add(expires, expire_ban, bd);
1388 bd->next = channel->bans;
1390 channel->bans->prev = bd;
1392 channel->banCount++;
1399 del_channel_ban(struct banData *ban)
1401 ban->channel->banCount--;
1405 ban->prev->next = ban->next;
1407 ban->channel->bans = ban->next;
1410 ban->next->prev = ban->prev;
1413 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1422 expire_ban(void *data)
1424 struct banData *bd = data;
1425 if(!IsSuspended(bd->channel))
1427 struct banList bans;
1428 struct mod_chanmode change;
1430 bans = bd->channel->channel->banlist;
1431 mod_chanmode_init(&change);
1432 for(ii=0; ii<bans.used; ii++)
1434 if(!strcmp(bans.list[ii]->ban, bd->mask))
1437 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1438 change.args[0].u.hostmask = bd->mask;
1439 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1445 del_channel_ban(bd);
1448 static void chanserv_expire_suspension(void *data);
1451 unregister_channel(struct chanData *channel, const char *reason)
1453 struct mod_chanmode change;
1454 char msgbuf[MAXLEN];
1457 /* After channel unregistration, the following must be cleaned
1459 - Channel information.
1462 - Channel suspension data.
1463 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1469 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1471 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1472 mod_chanmode_init(&change);
1474 change.modes_clear |= MODE_REGISTERED;
1475 if(chanserv_conf.revoke_mode_a)
1476 change.modes_clear |= MODE_ACCESS;
1477 mod_chanmode_announce(chanserv, channel->channel, &change);
1480 while(channel->users)
1481 del_channel_user(channel->users, 0);
1483 while(channel->bans)
1484 del_channel_ban(channel->bans);
1486 free(channel->topic);
1487 free(channel->registrar);
1488 free(channel->greeting);
1489 free(channel->user_greeting);
1490 free(channel->topic_mask);
1492 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1493 if(channel->advtopic[i])
1494 free(channel->advtopic[i]);
1498 channel->prev->next = channel->next;
1500 channelList = channel->next;
1503 channel->next->prev = channel->prev;
1505 if(channel->suspended)
1507 struct chanNode *cNode = channel->channel;
1508 struct suspended *suspended, *next_suspended;
1510 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1512 next_suspended = suspended->previous;
1513 free(suspended->suspender);
1514 free(suspended->reason);
1515 if(suspended->expires)
1516 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1521 cNode->channel_info = NULL;
1524 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1525 channel->channel->channel_info = NULL;
1527 dict_delete(channel->notes);
1528 if(!IsSuspended(channel))
1529 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1531 chanserv_oper_message(CSMSG_ALERT_UNREGISTERED, channel->channel->name, reason);
1532 UnlockChannel(channel->channel);
1534 registered_channels--;
1538 expire_channels(void *data)
1540 struct chanData *channel, *next;
1541 struct userData *user;
1542 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1544 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1545 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1547 for(channel = channelList; channel; channel = next)
1549 next = channel->next;
1551 /* See if the channel can be expired. */
1552 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1553 || IsProtected(channel))
1556 /* Make sure there are no high-ranking users still in the channel. */
1557 for(user=channel->users; user; user=user->next)
1558 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1563 /* Unregister the channel */
1564 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1565 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1566 unregister_channel(channel, "registration expired.");
1569 if(chanserv_conf.channel_expire_frequency && !data)
1570 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1574 expire_dnrs(UNUSED_ARG(void *data))
1576 dict_iterator_t it, next;
1577 struct do_not_register *dnr;
1579 for(it = dict_first(handle_dnrs); it; it = next)
1581 dnr = iter_data(it);
1582 next = iter_next(it);
1583 if(dnr->expires && dnr->expires <= now)
1584 dict_remove(handle_dnrs, dnr->chan_name + 1);
1586 for(it = dict_first(plain_dnrs); it; it = next)
1588 dnr = iter_data(it);
1589 next = iter_next(it);
1590 if(dnr->expires && dnr->expires <= now)
1591 dict_remove(plain_dnrs, dnr->chan_name + 1);
1593 for(it = dict_first(mask_dnrs); it; it = next)
1595 dnr = iter_data(it);
1596 next = iter_next(it);
1597 if(dnr->expires && dnr->expires <= now)
1598 dict_remove(mask_dnrs, dnr->chan_name + 1);
1601 if(chanserv_conf.dnr_expire_frequency)
1602 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1606 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1608 char protect = channel->chOpts[chProtect];
1609 struct userData *cs_victim, *cs_aggressor;
1611 /* Don't protect if no one is to be protected, someone is attacking
1612 himself, or if the aggressor is an IRC Operator. */
1613 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1616 /* Don't protect if the victim isn't authenticated (because they
1617 can't be a channel user), unless we are to protect non-users
1619 cs_victim = GetChannelAccess(channel, victim->handle_info);
1620 if(protect != 'a' && !cs_victim)
1623 /* Protect if the aggressor isn't a user because at this point,
1624 the aggressor can only be less than or equal to the victim. */
1625 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1629 /* If the aggressor was a user, then the victim can't be helped. */
1636 if(cs_victim->access > cs_aggressor->access)
1641 if(cs_victim->access >= cs_aggressor->access)
1650 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1652 struct chanData *cData = channel->channel_info;
1653 struct userData *cs_victim;
1655 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1656 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1657 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1659 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1667 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1669 if(IsService(victim))
1671 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1675 if(protect_user(victim, user, channel->channel_info))
1677 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1684 static struct do_not_register *
1685 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1687 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1688 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1689 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1690 strcpy(dnr->reason, reason);
1692 dnr->expires = expires;
1693 if(dnr->chan_name[0] == '*')
1694 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1695 else if(strpbrk(dnr->chan_name, "*?"))
1696 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1698 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1702 static struct dnrList
1703 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1705 struct dnrList list;
1706 dict_iterator_t it, next;
1707 struct do_not_register *dnr;
1709 dnrList_init(&list);
1711 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1713 if(dnr->expires && dnr->expires <= now)
1714 dict_remove(handle_dnrs, handle);
1715 else if(list.used < max)
1716 dnrList_append(&list, dnr);
1719 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1721 if(dnr->expires && dnr->expires <= now)
1722 dict_remove(plain_dnrs, chan_name);
1723 else if(list.used < max)
1724 dnrList_append(&list, dnr);
1729 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1731 next = iter_next(it);
1732 if(!match_ircglob(chan_name, iter_key(it)))
1734 dnr = iter_data(it);
1735 if(dnr->expires && dnr->expires <= now)
1736 dict_remove(mask_dnrs, iter_key(it));
1738 dnrList_append(&list, dnr);
1745 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1747 struct userNode *user;
1748 char buf1[INTERVALLEN];
1749 char buf2[INTERVALLEN];
1756 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1761 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1762 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1766 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1769 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1774 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1776 struct dnrList list;
1779 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1780 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1781 dnr_print_func(list.list[ii], user);
1783 reply("CSMSG_MORE_DNRS", list.used - ii);
1788 struct do_not_register *
1789 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1791 struct dnrList list;
1792 struct do_not_register *dnr;
1794 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1795 dnr = list.used ? list.list[0] : NULL;
1800 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1802 struct do_not_register *dnr;
1803 dict_iterator_t it, next;
1804 unsigned int matches = 0;
1806 for(it = dict_first(dict); it; it = next)
1808 dnr = iter_data(it);
1809 next = iter_next(it);
1810 if(dnr->expires && dnr->expires <= now)
1812 dict_remove(dict, iter_key(it));
1815 dnr_print_func(dnr, user);
1822 static CHANSERV_FUNC(cmd_noregister)
1826 unsigned long expiry, duration;
1827 unsigned int matches;
1831 reply("CSMSG_DNR_SEARCH_RESULTS");
1832 matches = send_dnrs(user, handle_dnrs);
1833 matches += send_dnrs(user, plain_dnrs);
1834 matches += send_dnrs(user, mask_dnrs);
1836 reply("MSG_MATCH_COUNT", matches);
1838 reply("MSG_NO_MATCHES");
1844 if(!IsChannelName(target) && (*target != '*'))
1846 reply("CSMSG_NOT_DNR", target);
1854 reply("MSG_INVALID_DURATION", argv[2]);
1858 if(!strcmp(argv[2], "0"))
1860 else if((duration = ParseInterval(argv[2])))
1861 expiry = now + duration;
1864 reply("MSG_INVALID_DURATION", argv[2]);
1868 reason = unsplit_string(argv + 3, argc - 3, NULL);
1869 if((*target == '*') && !get_handle_info(target + 1))
1871 reply("MSG_HANDLE_UNKNOWN", target + 1);
1874 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1875 reply("CSMSG_NOREGISTER_CHANNEL", target);
1879 reply("CSMSG_DNR_SEARCH_RESULTS");
1881 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1883 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1885 reply("MSG_NO_MATCHES");
1889 static CHANSERV_FUNC(cmd_allowregister)
1891 const char *chan_name = argv[1];
1893 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1894 || dict_remove(plain_dnrs, chan_name)
1895 || dict_remove(mask_dnrs, chan_name))
1897 reply("CSMSG_DNR_REMOVED", chan_name);
1900 reply("CSMSG_NO_SUCH_DNR", chan_name);
1905 struct userNode *source;
1909 unsigned long min_set, max_set;
1910 unsigned long min_expires, max_expires;
1915 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1917 return !((dnr->set < search->min_set)
1918 || (dnr->set > search->max_set)
1919 || (dnr->expires < search->min_expires)
1920 || (search->max_expires
1921 && ((dnr->expires == 0)
1922 || (dnr->expires > search->max_expires)))
1923 || (search->chan_mask
1924 && !match_ircglob(dnr->chan_name, search->chan_mask))
1925 || (search->setter_mask
1926 && !match_ircglob(dnr->setter, search->setter_mask))
1927 || (search->reason_mask
1928 && !match_ircglob(dnr->reason, search->reason_mask)));
1931 static struct dnr_search *
1932 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1934 struct dnr_search *discrim;
1937 discrim = calloc(1, sizeof(*discrim));
1938 discrim->source = user;
1939 discrim->chan_mask = NULL;
1940 discrim->setter_mask = NULL;
1941 discrim->reason_mask = NULL;
1942 discrim->max_set = INT_MAX;
1943 discrim->limit = 50;
1945 for(ii=0; ii<argc; ++ii)
1949 reply("MSG_MISSING_PARAMS", argv[ii]);
1952 else if(0 == irccasecmp(argv[ii], "channel"))
1954 discrim->chan_mask = argv[++ii];
1956 else if(0 == irccasecmp(argv[ii], "setter"))
1958 discrim->setter_mask = argv[++ii];
1960 else if(0 == irccasecmp(argv[ii], "reason"))
1962 discrim->reason_mask = argv[++ii];
1964 else if(0 == irccasecmp(argv[ii], "limit"))
1966 discrim->limit = strtoul(argv[++ii], NULL, 0);
1968 else if(0 == irccasecmp(argv[ii], "set"))
1970 const char *cmp = argv[++ii];
1973 discrim->min_set = now - ParseInterval(cmp + 2);
1975 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1976 } else if(cmp[0] == '=') {
1977 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1978 } else if(cmp[0] == '>') {
1980 discrim->max_set = now - ParseInterval(cmp + 2);
1982 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1984 discrim->max_set = now - (ParseInterval(cmp) - 1);
1987 else if(0 == irccasecmp(argv[ii], "expires"))
1989 const char *cmp = argv[++ii];
1992 discrim->max_expires = now + ParseInterval(cmp + 2);
1994 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1995 } else if(cmp[0] == '=') {
1996 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1997 } else if(cmp[0] == '>') {
1999 discrim->min_expires = now + ParseInterval(cmp + 2);
2001 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2003 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2008 reply("MSG_INVALID_CRITERIA", argv[ii]);
2019 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2022 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2024 struct do_not_register *dnr;
2025 dict_iterator_t next;
2030 /* Initialize local variables. */
2033 if(discrim->chan_mask)
2035 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2036 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2040 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2042 /* Check against account-based DNRs. */
2043 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2044 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2047 else if(target_fixed)
2049 /* Check against channel-based DNRs. */
2050 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2051 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2056 /* Exhaustively search account DNRs. */
2057 for(it = dict_first(handle_dnrs); it; it = next)
2059 next = iter_next(it);
2060 dnr = iter_data(it);
2061 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2065 /* Do the same for channel DNRs. */
2066 for(it = dict_first(plain_dnrs); it; it = next)
2068 next = iter_next(it);
2069 dnr = iter_data(it);
2070 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2074 /* Do the same for wildcarded channel DNRs. */
2075 for(it = dict_first(mask_dnrs); it; it = next)
2077 next = iter_next(it);
2078 dnr = iter_data(it);
2079 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2087 dnr_remove_func(struct do_not_register *match, void *extra)
2089 struct userNode *user;
2092 chan_name = alloca(strlen(match->chan_name) + 1);
2093 strcpy(chan_name, match->chan_name);
2095 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2096 || dict_remove(plain_dnrs, chan_name)
2097 || dict_remove(mask_dnrs, chan_name))
2099 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2105 dnr_count_func(struct do_not_register *match, void *extra)
2107 return 0; (void)match; (void)extra;
2110 static MODCMD_FUNC(cmd_dnrsearch)
2112 struct dnr_search *discrim;
2113 dnr_search_func action;
2114 struct svccmd *subcmd;
2115 unsigned int matches;
2118 sprintf(buf, "dnrsearch %s", argv[1]);
2119 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2122 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2125 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2127 if(!irccasecmp(argv[1], "print"))
2128 action = dnr_print_func;
2129 else if(!irccasecmp(argv[1], "remove"))
2130 action = dnr_remove_func;
2131 else if(!irccasecmp(argv[1], "count"))
2132 action = dnr_count_func;
2135 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2139 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2143 if(action == dnr_print_func)
2144 reply("CSMSG_DNR_SEARCH_RESULTS");
2145 matches = dnr_search(discrim, action, user);
2147 reply("MSG_MATCH_COUNT", matches);
2149 reply("MSG_NO_MATCHES");
2155 chanserv_get_owned_count(struct handle_info *hi)
2157 struct userData *cList;
2160 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2161 if(cList->access == UL_OWNER)
2166 static CHANSERV_FUNC(cmd_register)
2168 struct handle_info *handle;
2169 struct chanData *cData;
2170 struct modeNode *mn;
2171 char reason[MAXLEN];
2173 unsigned int new_channel, force=0;
2174 struct do_not_register *dnr;
2178 if(channel->channel_info)
2180 reply("CSMSG_ALREADY_REGGED", channel->name);
2184 if(channel->bad_channel)
2186 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2191 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2193 reply("CSMSG_MUST_BE_OPPED", channel->name);
2198 chan_name = channel->name;
2202 if((argc < 2) || !IsChannelName(argv[1]))
2204 reply("MSG_NOT_CHANNEL_NAME");
2208 if(opserv_bad_channel(argv[1]))
2210 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2215 chan_name = argv[1];
2218 if(argc >= (new_channel+2))
2220 if(!IsHelping(user))
2222 reply("CSMSG_PROXY_FORBIDDEN");
2226 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2228 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2229 dnr = chanserv_is_dnr(chan_name, handle);
2233 handle = user->handle_info;
2234 dnr = chanserv_is_dnr(chan_name, handle);
2238 if(!IsHelping(user))
2239 reply("CSMSG_DNR_CHANNEL", chan_name);
2241 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2245 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2247 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2252 channel = AddChannel(argv[1], now, NULL, NULL);
2254 cData = register_channel(channel, user->handle_info->handle);
2255 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2256 cData->modes = chanserv_conf.default_modes;
2258 cData->modes.modes_set |= MODE_REGISTERED;
2259 if (IsOffChannel(cData))
2261 mod_chanmode_announce(chanserv, channel, &cData->modes);
2265 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2266 change->args[change->argc].mode = MODE_CHANOP;
2267 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2269 mod_chanmode_announce(chanserv, channel, change);
2270 mod_chanmode_free(change);
2273 /* Initialize the channel's max user record. */
2274 cData->max = channel->members.used;
2275 cData->max_time = 0;
2277 if(handle != user->handle_info)
2278 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2280 reply("CSMSG_REG_SUCCESS", channel->name);
2282 chanserv_oper_message(CSMSG_ALERT_REGISTERED, channel->name, handle->handle, user->handle_info->handle);
2287 make_confirmation_string(struct userData *uData)
2289 static char strbuf[16];
2294 for(src = uData->handle->handle; *src; )
2295 accum = accum * 31 + toupper(*src++);
2297 for(src = uData->channel->channel->name; *src; )
2298 accum = accum * 31 + toupper(*src++);
2299 sprintf(strbuf, "%08x", accum);
2303 static CHANSERV_FUNC(cmd_unregister)
2306 char reason[MAXLEN];
2307 struct chanData *cData;
2308 struct userData *uData;
2310 cData = channel->channel_info;
2313 reply("CSMSG_NOT_REGISTERED", channel->name);
2317 uData = GetChannelUser(cData, user->handle_info);
2318 if(!uData || (uData->access < UL_OWNER))
2320 reply("CSMSG_NO_ACCESS");
2324 if(IsProtected(cData) && !IsOper(user))
2326 reply("CSMSG_UNREG_NODELETE", channel->name);
2330 if(!IsHelping(user))
2332 const char *confirm_string;
2333 if(IsSuspended(cData))
2335 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2338 confirm_string = make_confirmation_string(uData);
2339 if((argc < 2) || strcmp(argv[1], confirm_string))
2341 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2346 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2347 name = strdup(channel->name);
2348 unregister_channel(cData, reason);
2349 spamserv_cs_unregister(user, channel, manually, "unregistered");
2350 reply("CSMSG_UNREG_SUCCESS", name);
2356 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2358 extern struct userNode *spamserv;
2359 struct mod_chanmode *change;
2361 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2363 change = mod_chanmode_alloc(2);
2365 change->args[0].mode = MODE_CHANOP;
2366 change->args[0].u.member = AddChannelUser(chanserv, channel);
2367 change->args[1].mode = MODE_CHANOP;
2368 change->args[1].u.member = AddChannelUser(spamserv, channel);
2372 change = mod_chanmode_alloc(1);
2374 change->args[0].mode = MODE_CHANOP;
2375 change->args[0].u.member = AddChannelUser(chanserv, channel);
2378 mod_chanmode_announce(chanserv, channel, change);
2379 mod_chanmode_free(change);
2382 static CHANSERV_FUNC(cmd_move)
2384 struct mod_chanmode change;
2385 struct chanNode *target;
2386 struct modeNode *mn;
2387 struct userData *uData;
2388 char reason[MAXLEN];
2389 struct do_not_register *dnr;
2390 int chanserv_join = 0, spamserv_join;
2394 if(IsProtected(channel->channel_info))
2396 reply("CSMSG_MOVE_NODELETE", channel->name);
2400 if(!IsChannelName(argv[1]))
2402 reply("MSG_NOT_CHANNEL_NAME");
2406 if(opserv_bad_channel(argv[1]))
2408 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2412 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2414 for(uData = channel->channel_info->users; uData; uData = uData->next)
2416 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2418 if(!IsHelping(user))
2419 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2421 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2427 mod_chanmode_init(&change);
2428 if(!(target = GetChannel(argv[1])))
2430 target = AddChannel(argv[1], now, NULL, NULL);
2431 if(!IsSuspended(channel->channel_info))
2434 else if(target->channel_info)
2436 reply("CSMSG_ALREADY_REGGED", target->name);
2439 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2440 && !IsHelping(user))
2442 reply("CSMSG_MUST_BE_OPPED", target->name);
2445 else if(!IsSuspended(channel->channel_info))
2450 /* Clear MODE_REGISTERED from old channel, add it to new. */
2452 change.modes_clear = MODE_REGISTERED;
2453 mod_chanmode_announce(chanserv, channel, &change);
2454 change.modes_clear = 0;
2455 change.modes_set = MODE_REGISTERED;
2456 mod_chanmode_announce(chanserv, target, &change);
2459 /* Move the channel_info to the target channel; it
2460 shouldn't be necessary to clear timeq callbacks
2461 for the old channel. */
2462 target->channel_info = channel->channel_info;
2463 target->channel_info->channel = target;
2464 channel->channel_info = NULL;
2466 /* Check whether users are present in the new channel. */
2467 for(uData = target->channel_info->users; uData; uData = uData->next)
2468 scan_user_presence(uData, NULL);
2470 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2473 ss_cs_join_channel(target, spamserv_join);
2475 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2476 if(!IsSuspended(target->channel_info))
2478 char reason2[MAXLEN];
2479 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2480 DelChannelUser(chanserv, channel, reason2, 0);
2482 UnlockChannel(channel);
2483 LockChannel(target);
2484 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2485 reply("CSMSG_MOVE_SUCCESS", target->name);
2490 merge_users(struct chanData *source, struct chanData *target)
2492 struct userData *suData, *tuData, *next;
2498 /* Insert the source's users into the scratch area. */
2499 for(suData = source->users; suData; suData = suData->next)
2500 dict_insert(merge, suData->handle->handle, suData);
2502 /* Iterate through the target's users, looking for
2503 users common to both channels. The lower access is
2504 removed from either the scratch area or target user
2506 for(tuData = target->users; tuData; tuData = next)
2508 struct userData *choice;
2510 next = tuData->next;
2512 /* If a source user exists with the same handle as a target
2513 channel's user, resolve the conflict by removing one. */
2514 suData = dict_find(merge, tuData->handle->handle, NULL);
2518 /* Pick the data we want to keep. */
2519 /* If the access is the same, use the later seen time. */
2520 if(suData->access == tuData->access)
2521 choice = (suData->seen > tuData->seen) ? suData : tuData;
2522 else /* Otherwise, keep the higher access level. */
2523 choice = (suData->access > tuData->access) ? suData : tuData;
2524 /* Use the later seen time. */
2525 if(suData->seen < tuData->seen)
2526 suData->seen = tuData->seen;
2528 tuData->seen = suData->seen;
2530 /* Remove the user that wasn't picked. */
2531 if(choice == tuData)
2533 dict_remove(merge, suData->handle->handle);
2534 del_channel_user(suData, 0);
2537 del_channel_user(tuData, 0);
2540 /* Move the remaining users to the target channel. */
2541 for(it = dict_first(merge); it; it = iter_next(it))
2543 suData = iter_data(it);
2545 /* Insert the user into the target channel's linked list. */
2546 suData->prev = NULL;
2547 suData->next = target->users;
2548 suData->channel = target;
2551 target->users->prev = suData;
2552 target->users = suData;
2554 /* Update the user counts for the target channel; the
2555 source counts are left alone. */
2556 target->userCount++;
2558 /* Check whether the user is in the target channel. */
2559 scan_user_presence(suData, NULL);
2562 /* Possible to assert (source->users == NULL) here. */
2563 source->users = NULL;
2568 merge_bans(struct chanData *source, struct chanData *target)
2570 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2572 /* Hold on to the original head of the target ban list
2573 to avoid comparing source bans with source bans. */
2574 tFront = target->bans;
2576 /* Perform a totally expensive O(n*m) merge, ick. */
2577 for(sbData = source->bans; sbData; sbData = sNext)
2579 /* Flag to track whether the ban's been moved
2580 to the destination yet. */
2583 /* Possible to assert (sbData->prev == NULL) here. */
2584 sNext = sbData->next;
2586 for(tbData = tFront; tbData; tbData = tNext)
2588 tNext = tbData->next;
2590 /* Perform two comparisons between each source
2591 and target ban, conflicts are resolved by
2592 keeping the broader ban and copying the later
2593 expiration and triggered time. */
2594 if(match_ircglobs(tbData->mask, sbData->mask))
2596 /* There is a broader ban in the target channel that
2597 overrides one in the source channel; remove the
2598 source ban and break. */
2599 if(sbData->expires > tbData->expires)
2600 tbData->expires = sbData->expires;
2601 if(sbData->triggered > tbData->triggered)
2602 tbData->triggered = sbData->triggered;
2603 del_channel_ban(sbData);
2606 else if(match_ircglobs(sbData->mask, tbData->mask))
2608 /* There is a broader ban in the source channel that
2609 overrides one in the target channel; remove the
2610 target ban, fall through and move the source over. */
2611 if(tbData->expires > sbData->expires)
2612 sbData->expires = tbData->expires;
2613 if(tbData->triggered > sbData->triggered)
2614 sbData->triggered = tbData->triggered;
2615 if(tbData == tFront)
2617 del_channel_ban(tbData);
2620 /* Source bans can override multiple target bans, so
2621 we allow a source to run through this loop multiple
2622 times, but we can only move it once. */
2627 /* Remove the source ban from the source ban list. */
2629 sbData->next->prev = sbData->prev;
2631 /* Modify the source ban's associated channel. */
2632 sbData->channel = target;
2634 /* Insert the ban into the target channel's linked list. */
2635 sbData->prev = NULL;
2636 sbData->next = target->bans;
2639 target->bans->prev = sbData;
2640 target->bans = sbData;
2642 /* Update the user counts for the target channel. */
2647 /* Possible to assert (source->bans == NULL) here. */
2648 source->bans = NULL;
2652 merge_data(struct chanData *source, struct chanData *target)
2654 /* Use more recent visited and owner-transfer time; use older
2655 * registered time. Bitwise or may_opchan. Use higher max.
2656 * Do not touch last_refresh, ban count or user counts.
2658 if(source->visited > target->visited)
2659 target->visited = source->visited;
2660 if(source->registered < target->registered)
2661 target->registered = source->registered;
2662 if(source->ownerTransfer > target->ownerTransfer)
2663 target->ownerTransfer = source->ownerTransfer;
2664 if(source->may_opchan)
2665 target->may_opchan = 1;
2666 if(source->max > target->max) {
2667 target->max = source->max;
2668 target->max_time = source->max_time;
2673 merge_channel(struct chanData *source, struct chanData *target)
2675 merge_users(source, target);
2676 merge_bans(source, target);
2677 merge_data(source, target);
2680 static CHANSERV_FUNC(cmd_merge)
2682 struct userData *target_user;
2683 struct chanNode *target;
2684 char reason[MAXLEN];
2688 /* Make sure the target channel exists and is registered to the user
2689 performing the command. */
2690 if(!(target = GetChannel(argv[1])))
2692 reply("MSG_INVALID_CHANNEL");
2696 if(!target->channel_info)
2698 reply("CSMSG_NOT_REGISTERED", target->name);
2702 if(IsProtected(channel->channel_info))
2704 reply("CSMSG_MERGE_NODELETE");
2708 if(IsSuspended(target->channel_info))
2710 reply("CSMSG_MERGE_SUSPENDED");
2714 if(channel == target)
2716 reply("CSMSG_MERGE_SELF");
2720 target_user = GetChannelUser(target->channel_info, user->handle_info);
2721 if(!target_user || (target_user->access < UL_OWNER))
2723 reply("CSMSG_MERGE_NOT_OWNER");
2727 /* Merge the channel structures and associated data. */
2728 merge_channel(channel->channel_info, target->channel_info);
2729 spamserv_cs_move_merge(user, channel, target, 0);
2730 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2731 unregister_channel(channel->channel_info, reason);
2732 reply("CSMSG_MERGE_SUCCESS", target->name);
2736 static CHANSERV_FUNC(cmd_opchan)
2738 struct mod_chanmode change;
2739 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2741 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2744 channel->channel_info->may_opchan = 0;
2745 mod_chanmode_init(&change);
2747 change.args[0].mode = MODE_CHANOP;
2748 change.args[0].u.member = GetUserMode(channel, chanserv);
2749 if(!change.args[0].u.member)
2751 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2754 mod_chanmode_announce(chanserv, channel, &change);
2755 reply("CSMSG_OPCHAN_DONE", channel->name);
2759 static CHANSERV_FUNC(cmd_adduser)
2761 struct userData *actee;
2762 struct userData *actor, *real_actor;
2763 struct handle_info *handle;
2764 unsigned short access_level, override = 0;
2768 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2770 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2774 access_level = user_level_from_name(argv[2], UL_OWNER);
2777 reply("CSMSG_INVALID_ACCESS", argv[2]);
2781 actor = GetChannelUser(channel->channel_info, user->handle_info);
2782 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2784 if(actor->access <= access_level)
2786 reply("CSMSG_NO_BUMP_ACCESS");
2790 /* Trying to add someone with equal/more access? */
2791 if (!real_actor || real_actor->access <= access_level)
2792 override = CMD_LOG_OVERRIDE;
2794 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2797 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2799 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2803 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2804 scan_user_presence(actee, NULL);
2805 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2806 return 1 | override;
2809 static CHANSERV_FUNC(cmd_clvl)
2811 struct handle_info *handle;
2812 struct userData *victim;
2813 struct userData *actor, *real_actor;
2814 unsigned short new_access, override = 0;
2815 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2819 actor = GetChannelUser(channel->channel_info, user->handle_info);
2820 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2822 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2825 if(handle == user->handle_info && !privileged)
2827 reply("CSMSG_NO_SELF_CLVL");
2831 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2833 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2837 if(actor->access <= victim->access && !privileged)
2839 reply("MSG_USER_OUTRANKED", handle->handle);
2843 new_access = user_level_from_name(argv[2], UL_OWNER);
2847 reply("CSMSG_INVALID_ACCESS", argv[2]);
2851 if(new_access >= actor->access && !privileged)
2853 reply("CSMSG_NO_BUMP_ACCESS");
2857 /* Trying to clvl a equal/higher user? */
2858 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2859 override = CMD_LOG_OVERRIDE;
2860 /* Trying to clvl someone to equal/higher access? */
2861 if(!real_actor || new_access >= real_actor->access)
2862 override = CMD_LOG_OVERRIDE;
2863 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2864 * If they lower their own access it's not a big problem.
2867 victim->access = new_access;
2868 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2869 return 1 | override;
2872 static CHANSERV_FUNC(cmd_deluser)
2874 struct handle_info *handle;
2875 struct userData *victim;
2876 struct userData *actor, *real_actor;
2877 unsigned short access_level, override = 0;
2882 actor = GetChannelUser(channel->channel_info, user->handle_info);
2883 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2885 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2888 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2890 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2896 access_level = user_level_from_name(argv[1], UL_OWNER);
2899 reply("CSMSG_INVALID_ACCESS", argv[1]);
2902 if(access_level != victim->access)
2904 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2910 access_level = victim->access;
2913 if((actor->access <= victim->access) && !IsHelping(user))
2915 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2919 /* If people delete themselves it is an override, but they
2920 * could've used deleteme so we don't log it as an override
2922 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2923 override = CMD_LOG_OVERRIDE;
2925 chan_name = strdup(channel->name);
2926 del_channel_user(victim, 1);
2927 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2929 return 1 | override;
2933 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2935 struct userData *actor, *real_actor, *uData, *next;
2936 unsigned int override = 0;
2938 actor = GetChannelUser(channel->channel_info, user->handle_info);
2939 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2941 if(min_access > max_access)
2943 reply("CSMSG_BAD_RANGE", min_access, max_access);
2947 if(actor->access <= max_access)
2949 reply("CSMSG_NO_ACCESS");
2953 if(!real_actor || real_actor->access <= max_access)
2954 override = CMD_LOG_OVERRIDE;
2956 for(uData = channel->channel_info->users; uData; uData = next)
2960 if((uData->access >= min_access)
2961 && (uData->access <= max_access)
2962 && match_ircglob(uData->handle->handle, mask))
2963 del_channel_user(uData, 1);
2966 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2967 return 1 | override;
2970 static CHANSERV_FUNC(cmd_mdelowner)
2972 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2975 static CHANSERV_FUNC(cmd_mdelcoowner)
2977 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2980 static CHANSERV_FUNC(cmd_mdelmaster)
2982 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2985 static CHANSERV_FUNC(cmd_mdelop)
2987 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2990 static CHANSERV_FUNC(cmd_mdelpeon)
2992 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2996 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2998 struct banData *bData, *next;
2999 char interval[INTERVALLEN];
3001 unsigned long limit;
3004 limit = now - duration;
3005 for(bData = channel->channel_info->bans; bData; bData = next)
3009 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3012 del_channel_ban(bData);
3016 intervalString(interval, duration, user->handle_info);
3017 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3022 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3024 struct userData *actor, *uData, *next;
3025 char interval[INTERVALLEN];
3027 unsigned long limit;
3029 actor = GetChannelUser(channel->channel_info, user->handle_info);
3030 if(min_access > max_access)
3032 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3036 if(!actor || actor->access <= max_access)
3038 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3043 limit = now - duration;
3044 for(uData = channel->channel_info->users; uData; uData = next)
3048 if((uData->seen > limit)
3050 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3053 if(((uData->access >= min_access) && (uData->access <= max_access))
3054 || (!max_access && (uData->access < actor->access)))
3056 del_channel_user(uData, 1);
3064 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3066 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3070 static CHANSERV_FUNC(cmd_trim)
3072 unsigned long duration;
3073 unsigned short min_level, max_level;
3078 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3079 duration = ParseInterval(argv[2]);
3082 reply("CSMSG_CANNOT_TRIM");
3086 if(!irccasecmp(argv[1], "bans"))
3088 cmd_trim_bans(user, channel, duration);
3091 else if(!irccasecmp(argv[1], "users"))
3093 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3096 else if(parse_level_range(&min_level, &max_level, argv[1]))
3098 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3101 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3103 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3108 reply("CSMSG_INVALID_TRIM", argv[1]);
3113 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3114 to the user. cmd_all takes advantage of this. */
3115 static CHANSERV_FUNC(cmd_up)
3117 struct mod_chanmode change;
3118 struct userData *uData;
3121 mod_chanmode_init(&change);
3123 change.args[0].u.member = GetUserMode(channel, user);
3124 if(!change.args[0].u.member)
3127 reply("MSG_CHANNEL_ABSENT", channel->name);
3131 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3135 reply("CSMSG_GODMODE_UP", argv[0]);
3138 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3140 change.args[0].mode = MODE_CHANOP;
3141 errmsg = "CSMSG_ALREADY_OPPED";
3143 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3145 change.args[0].mode = MODE_VOICE;
3146 errmsg = "CSMSG_ALREADY_VOICED";
3151 reply("CSMSG_NO_ACCESS");
3154 change.args[0].mode &= ~change.args[0].u.member->modes;
3155 if(!change.args[0].mode)
3158 reply(errmsg, channel->name);
3161 modcmd_chanmode_announce(&change);
3165 static CHANSERV_FUNC(cmd_down)
3167 struct mod_chanmode change;
3169 mod_chanmode_init(&change);
3171 change.args[0].u.member = GetUserMode(channel, user);
3172 if(!change.args[0].u.member)
3175 reply("MSG_CHANNEL_ABSENT", channel->name);
3179 if(!change.args[0].u.member->modes)
3182 reply("CSMSG_ALREADY_DOWN", channel->name);
3186 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3187 modcmd_chanmode_announce(&change);
3191 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3193 struct userData *cList;
3195 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3197 if(IsSuspended(cList->channel)
3198 || IsUserSuspended(cList)
3199 || !GetUserMode(cList->channel->channel, user))
3202 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3208 static CHANSERV_FUNC(cmd_upall)
3210 return cmd_all(CSFUNC_ARGS, cmd_up);
3213 static CHANSERV_FUNC(cmd_downall)
3215 return cmd_all(CSFUNC_ARGS, cmd_down);
3218 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3219 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3222 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3224 unsigned int ii, valid;
3225 struct userNode *victim;
3226 struct mod_chanmode *change;
3228 change = mod_chanmode_alloc(argc - 1);
3230 for(ii=valid=0; ++ii < argc; )
3232 if(!(victim = GetUserH(argv[ii])))
3234 change->args[valid].mode = mode;
3235 change->args[valid].u.member = GetUserMode(channel, victim);
3236 if(!change->args[valid].u.member)
3238 if(validate && !validate(user, channel, victim))
3243 change->argc = valid;
3244 if(valid < (argc-1))
3245 reply("CSMSG_PROCESS_FAILED");
3248 modcmd_chanmode_announce(change);
3249 reply(action, channel->name);
3251 mod_chanmode_free(change);
3255 static CHANSERV_FUNC(cmd_op)
3257 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3260 static CHANSERV_FUNC(cmd_deop)
3262 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3265 static CHANSERV_FUNC(cmd_voice)
3267 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3270 static CHANSERV_FUNC(cmd_devoice)
3272 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3275 static CHANSERV_FUNC(cmd_opme)
3277 struct mod_chanmode change;
3280 mod_chanmode_init(&change);
3282 change.args[0].u.member = GetUserMode(channel, user);
3283 if(!change.args[0].u.member)
3286 reply("MSG_CHANNEL_ABSENT", channel->name);
3290 struct devnull_class *devnull;
3291 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3293 change.args[0].mode = MODE_CHANOP;
3294 errmsg = "CSMSG_ALREADY_OPPED";
3299 reply("CSMSG_NO_ACCESS");
3302 change.args[0].mode &= ~change.args[0].u.member->modes;
3303 if(!change.args[0].mode)
3306 reply(errmsg, channel->name);
3309 modcmd_chanmode_announce(&change);
3314 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3320 for(ii=0; ii<channel->members.used; ii++)
3322 struct modeNode *mn = channel->members.list[ii];
3324 if(IsService(mn->user))
3327 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3330 if(protect_user(mn->user, user, channel->channel_info))
3334 victims[(*victimCount)++] = mn;
3340 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3342 struct userNode *victim;
3343 struct modeNode **victims;
3344 unsigned int offset, n, victimCount, duration = 0;
3345 char *reason = "Bye.", *ban, *name;
3346 char interval[INTERVALLEN];
3348 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3349 REQUIRE_PARAMS(offset);
3350 if(argc > offset && IsNetServ(user))
3352 if(*argv[offset] == '$') {
3353 struct userNode *hib;
3354 const char *accountnameb = argv[offset] + 1;
3355 if(!(hib = GetUserH(accountnameb)))
3357 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3366 reason = unsplit_string(argv + offset, argc - offset, NULL);
3367 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3369 /* Truncate the reason to a length of TOPICLEN, as
3370 the ircd does; however, leave room for an ellipsis
3371 and the kicker's nick. */
3372 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3376 if((victim = GetUserH(argv[1])))
3378 victims = alloca(sizeof(victims[0]));
3379 victims[0] = GetUserMode(channel, victim);
3380 /* XXX: The comparison with ACTION_KICK is just because all
3381 * other actions can work on users outside the channel, and we
3382 * want to allow those (e.g. unbans) in that case. If we add
3383 * some other ejection action for in-channel users, change
3385 victimCount = victims[0] ? 1 : 0;
3387 if(IsService(victim))
3389 reply("MSG_SERVICE_IMMUNE", victim->nick);
3393 if((action == ACTION_KICK) && !victimCount)
3395 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3399 if(protect_user(victim, user, channel->channel_info))
3401 reply("CSMSG_USER_PROTECTED", victim->nick);
3405 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3406 name = victim->nick;
3408 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3410 struct handle_info *hi;
3411 extern const char *titlehost_suffix;
3412 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3413 const char *accountname = argv[1] + 1;
3415 if(!(hi = get_handle_info(accountname)))
3417 reply("MSG_HANDLE_UNKNOWN", accountname);
3421 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3422 victims = alloca(sizeof(victims[0]) * channel->members.used);
3424 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3426 reply("CSMSG_MASK_PROTECTED", banmask);
3430 if((action == ACTION_KICK) && (victimCount == 0))
3432 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3436 name = ban = strdup(banmask);
3440 if(!is_ircmask(argv[1]))
3442 reply("MSG_NICK_UNKNOWN", argv[1]);
3446 victims = alloca(sizeof(victims[0]) * channel->members.used);
3448 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3450 reply("CSMSG_MASK_PROTECTED", argv[1]);
3454 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3456 reply("CSMSG_LAME_MASK", argv[1]);
3460 if((action == ACTION_KICK) && (victimCount == 0))
3462 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3466 name = ban = strdup(argv[1]);
3469 /* Truncate the ban in place if necessary; we must ensure
3470 that 'ban' is a valid ban mask before sanitizing it. */
3471 sanitize_ircmask(ban);
3473 if(action & ACTION_ADD_BAN)
3475 struct banData *bData, *next;
3477 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3479 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3484 if(action & ACTION_ADD_TIMED_BAN)
3486 duration = ParseInterval(argv[2]);
3488 if(duration < chanserv_conf.min_time_bans)
3490 reply("CSMSG_DURATION_TOO_LOW");
3494 else if(duration > (86400 * 365 * 2))
3496 reply("CSMSG_DURATION_TOO_HIGH");
3502 for(bData = channel->channel_info->bans; bData; bData = next)
3504 if(match_ircglobs(bData->mask, ban))
3506 int exact = !irccasecmp(bData->mask, ban);
3508 /* The ban is redundant; there is already a ban
3509 with the same effect in place. */
3513 free(bData->reason);
3514 bData->reason = strdup(reason);
3515 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3517 reply("CSMSG_REASON_CHANGE", ban);
3521 if(exact && bData->expires)
3525 /* If the ban matches an existing one exactly,
3526 extend the expiration time if the provided
3527 duration is longer. */
3528 if(duration && (now + duration > bData->expires))
3530 bData->expires = now + duration;
3541 /* Delete the expiration timeq entry and
3542 requeue if necessary. */
3543 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3546 timeq_add(bData->expires, expire_ban, bData);
3550 /* automated kickban */
3553 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3555 reply("CSMSG_BAN_ADDED", name, channel->name);
3561 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3568 if(match_ircglobs(ban, bData->mask))
3570 /* The ban we are adding makes previously existing
3571 bans redundant; silently remove them. */
3572 del_channel_ban(bData);
3576 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);
3578 name = ban = strdup(bData->mask);
3582 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3584 extern const char *hidden_host_suffix;
3585 const char *old_name = chanserv_conf.old_ban_names->list[n];
3587 unsigned int l1, l2;
3590 l2 = strlen(old_name);
3593 if(irccasecmp(ban + l1 - l2, old_name))
3595 new_mask = malloc(MAXLEN);
3596 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3598 name = ban = new_mask;
3603 if(action & ACTION_BAN)
3605 unsigned int exists;
3606 struct mod_chanmode *change;
3608 if(channel->banlist.used >= MAXBANS)
3611 reply("CSMSG_BANLIST_FULL", channel->name);
3616 exists = ChannelBanExists(channel, ban);
3617 change = mod_chanmode_alloc(victimCount + 1);
3618 for(n = 0; n < victimCount; ++n)
3620 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3621 change->args[n].u.member = victims[n];
3625 change->args[n].mode = MODE_BAN;
3626 change->args[n++].u.hostmask = ban;
3630 modcmd_chanmode_announce(change);
3632 mod_chanmode_announce(chanserv, channel, change);
3633 mod_chanmode_free(change);
3635 if(exists && (action == ACTION_BAN))
3638 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3644 if(action & ACTION_KICK)
3646 char kick_reason[MAXLEN];
3647 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3649 for(n = 0; n < victimCount; n++)
3650 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3655 /* No response, since it was automated. */
3657 else if(action & ACTION_ADD_BAN)
3660 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3662 reply("CSMSG_BAN_ADDED", name, channel->name);
3664 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3665 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3666 else if(action & ACTION_BAN)
3667 reply("CSMSG_BAN_DONE", name, channel->name);
3668 else if(action & ACTION_KICK && victimCount)
3669 reply("CSMSG_KICK_DONE", name, channel->name);
3675 static CHANSERV_FUNC(cmd_kickban)
3677 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3680 static CHANSERV_FUNC(cmd_kick)
3682 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3685 static CHANSERV_FUNC(cmd_ban)
3687 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3690 static CHANSERV_FUNC(cmd_addban)
3692 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3695 static CHANSERV_FUNC(cmd_addtimedban)
3697 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3700 struct mod_chanmode *
3701 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3703 struct mod_chanmode *change;
3704 unsigned char *match;
3705 unsigned int ii, count;
3707 match = alloca(bans->used);
3710 for(ii = count = 0; ii < bans->used; ++ii)
3712 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3713 MATCH_USENICK | MATCH_VISIBLE);
3720 for(ii = count = 0; ii < bans->used; ++ii)
3722 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3729 change = mod_chanmode_alloc(count);
3730 for(ii = count = 0; ii < bans->used; ++ii)
3734 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3735 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3737 assert(count == change->argc);
3742 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3744 struct userNode *actee;
3750 /* may want to allow a comma delimited list of users... */
3751 if(!(actee = GetUserH(argv[1])))
3753 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3755 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3756 const char *accountname = argv[1] + 1;
3758 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3759 mask = strdup(banmask);
3761 else if(!is_ircmask(argv[1]))
3763 reply("MSG_NICK_UNKNOWN", argv[1]);
3768 mask = strdup(argv[1]);
3772 /* We don't sanitize the mask here because ircu
3774 if(action & ACTION_UNBAN)
3776 struct mod_chanmode *change;
3777 change = find_matching_bans(&channel->banlist, actee, mask);
3782 modcmd_chanmode_announce(change);
3783 for(ii = 0; ii < change->argc; ++ii)
3784 free((char*)change->args[ii].u.hostmask);
3785 mod_chanmode_free(change);
3790 if(action & ACTION_DEL_BAN)
3792 struct banData *ban, *next;
3794 ban = channel->channel_info->bans;
3798 for( ; ban && !user_matches_glob(actee, ban->mask,
3799 MATCH_USENICK | MATCH_VISIBLE);
3802 for( ; ban && !match_ircglobs(mask, ban->mask);
3807 del_channel_ban(ban);
3814 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3816 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3822 static CHANSERV_FUNC(cmd_unban)
3824 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3827 static CHANSERV_FUNC(cmd_delban)
3829 /* it doesn't necessarily have to remove the channel ban - may want
3830 to make that an option. */
3831 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3834 static CHANSERV_FUNC(cmd_unbanme)
3836 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3837 long flags = ACTION_UNBAN;
3839 /* remove permanent bans if the user has the proper access. */
3840 if(uData->access >= UL_MASTER)
3841 flags |= ACTION_DEL_BAN;
3843 argv[1] = user->nick;
3844 return unban_user(user, channel, 2, argv, cmd, flags);
3847 static CHANSERV_FUNC(cmd_unbanall)
3849 struct mod_chanmode *change;
3852 if(!channel->banlist.used)
3854 reply("CSMSG_NO_BANS", channel->name);
3858 change = mod_chanmode_alloc(channel->banlist.used);
3859 for(ii=0; ii<channel->banlist.used; ii++)
3861 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3862 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3864 modcmd_chanmode_announce(change);
3865 for(ii = 0; ii < change->argc; ++ii)
3866 free((char*)change->args[ii].u.hostmask);
3867 mod_chanmode_free(change);
3868 reply("CSMSG_BANS_REMOVED", channel->name);
3872 static CHANSERV_FUNC(cmd_open)
3874 struct mod_chanmode *change;
3877 change = find_matching_bans(&channel->banlist, user, NULL);
3879 change = mod_chanmode_alloc(0);
3880 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3881 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3882 && channel->channel_info->modes.modes_set)
3883 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3884 modcmd_chanmode_announce(change);
3885 reply("CSMSG_CHANNEL_OPENED", channel->name);
3886 for(ii = 0; ii < change->argc; ++ii)
3887 free((char*)change->args[ii].u.hostmask);
3888 mod_chanmode_free(change);
3892 static CHANSERV_FUNC(cmd_myaccess)
3894 static struct string_buffer sbuf;
3895 struct handle_info *target_handle;
3896 struct userData *uData;
3901 target_handle = user->handle_info;
3902 else if(!IsStaff(user))
3904 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3907 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3910 if(!oper_outranks(user, target_handle))
3913 if(!target_handle->channels)
3915 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3919 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3920 for(uData = target_handle->channels; uData; uData = uData->u_next)
3922 struct chanData *cData = uData->channel;
3924 unsigned int base_len;
3926 if(uData->access > UL_OWNER)
3928 if(uData->access == UL_OWNER)
3931 if(IsProtected(cData)
3932 && (target_handle != user->handle_info)
3933 && !GetTrueChannelAccess(cData, user->handle_info)
3934 && !IsNetworkHelper(user))
3937 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3938 base_len = sbuf.used;
3939 if(IsUserSuspended(uData))
3940 string_buffer_append(&sbuf, 's');
3941 if(IsUserAutoOp(uData))
3943 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3944 string_buffer_append(&sbuf, 'o');
3945 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3946 string_buffer_append(&sbuf, 'v');
3948 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3949 string_buffer_append(&sbuf, 'i');
3950 if(sbuf.used==base_len)
3953 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3955 string_buffer_append_string(&sbuf, ")]");
3956 string_buffer_append(&sbuf, '\0');
3957 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3961 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3963 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3969 static CHANSERV_FUNC(cmd_access)
3971 struct userNode *target;
3972 struct handle_info *target_handle;
3973 struct userData *uData;
3975 char prefix[MAXLEN];
3980 target_handle = target->handle_info;
3982 else if((target = GetUserH(argv[1])))
3984 target_handle = target->handle_info;
3986 else if(argv[1][0] == '*')
3988 if(!(target_handle = get_handle_info(argv[1]+1)))
3990 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3996 reply("MSG_NICK_UNKNOWN", argv[1]);
4000 assert(target || target_handle);
4002 if(target == chanserv)
4004 reply("CSMSG_IS_CHANSERV");
4012 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4017 reply("MSG_USER_AUTHENTICATE", target->nick);
4020 reply("MSG_AUTHENTICATE");
4026 const char *epithet = NULL, *type = NULL;
4029 epithet = chanserv_conf.irc_operator_epithet;
4030 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4032 else if(IsNetworkHelper(target))
4034 epithet = chanserv_conf.network_helper_epithet;
4035 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4037 else if(IsSupportHelper(target))
4039 epithet = chanserv_conf.support_helper_epithet;
4040 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4044 if(target_handle->epithet)
4045 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4047 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4049 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4053 sprintf(prefix, "%s", target_handle->handle);
4056 if(!channel->channel_info)
4058 reply("CSMSG_NOT_REGISTERED", channel->name);
4062 helping = HANDLE_FLAGGED(target_handle, HELPING)
4063 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4064 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4066 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4067 /* To prevent possible information leaks, only show infolines
4068 * if the requestor is in the channel or it's their own
4070 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4072 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4074 /* Likewise, only say it's suspended if the user has active
4075 * access in that channel or it's their own entry. */
4076 if(IsUserSuspended(uData)
4077 && (GetChannelUser(channel->channel_info, user->handle_info)
4078 || (user->handle_info == uData->handle)))
4080 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4085 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4092 def_list(struct listData *list)
4096 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4098 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4099 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4100 if(list->table.length == 1)
4102 msg = user_find_message(list->user, "MSG_NONE");
4103 send_message_type(4, list->user, list->bot, " %s", msg);
4108 userData_access_comp(const void *arg_a, const void *arg_b)
4110 const struct userData *a = *(struct userData**)arg_a;
4111 const struct userData *b = *(struct userData**)arg_b;
4113 if(a->access != b->access)
4114 res = b->access - a->access;
4116 res = irccasecmp(a->handle->handle, b->handle->handle);
4121 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4123 void (*send_list)(struct listData *);
4124 struct userData *uData;
4125 struct listData lData;
4126 unsigned int matches;
4130 lData.bot = cmd->parent->bot;
4131 lData.channel = channel;
4132 lData.lowest = lowest;
4133 lData.highest = highest;
4134 lData.search = (argc > 1) ? argv[1] : NULL;
4135 send_list = def_list;
4137 if(user->handle_info)
4139 switch(user->handle_info->userlist_style)
4141 case HI_STYLE_DEF: send_list = def_list; break;
4142 case HI_STYLE_ZOOT: send_list = def_list; break;
4146 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4148 for(uData = channel->channel_info->users; uData; uData = uData->next)
4150 if((uData->access < lowest)
4151 || (uData->access > highest)
4152 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4154 lData.users[matches++] = uData;
4156 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4158 lData.table.length = matches+1;
4159 lData.table.width = 4;
4160 lData.table.flags = TABLE_NO_FREE;
4161 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4162 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4163 lData.table.contents[0] = ary;
4166 ary[2] = "Last Seen";
4168 for(matches = 1; matches < lData.table.length; ++matches)
4170 char seen[INTERVALLEN];
4172 uData = lData.users[matches-1];
4173 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4174 lData.table.contents[matches] = ary;
4175 ary[0] = strtab(uData->access);
4176 ary[1] = uData->handle->handle;
4179 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4181 else if(!uData->seen)
4184 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4185 ary[2] = strdup(ary[2]);
4186 if(IsUserSuspended(uData))
4187 ary[3] = "Suspended";
4188 else if(HANDLE_FLAGGED(uData->handle, OPER))
4189 ary[3] = "Operator";
4190 else if(HANDLE_FLAGGED(uData->handle, HELPING))
4192 else if(HANDLE_FLAGGED(uData->handle, NETWORK))
4194 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4195 ary[3] = "Vacation";
4196 else if(HANDLE_FLAGGED(uData->handle, BOT))
4202 for(matches = 1; matches < lData.table.length; ++matches)
4204 free((char*)lData.table.contents[matches][2]);
4205 free(lData.table.contents[matches]);
4207 free(lData.table.contents[0]);
4208 free(lData.table.contents);
4209 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4213 static CHANSERV_FUNC(cmd_users)
4215 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4218 static CHANSERV_FUNC(cmd_wlist)
4220 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4223 static CHANSERV_FUNC(cmd_clist)
4225 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4228 static CHANSERV_FUNC(cmd_mlist)
4230 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4233 static CHANSERV_FUNC(cmd_olist)
4235 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4238 static CHANSERV_FUNC(cmd_plist)
4240 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4243 static CHANSERV_FUNC(cmd_bans)
4245 struct userNode *search_u = NULL;
4246 struct helpfile_table tbl;
4247 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4248 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4249 const char *msg_never, *triggered, *expires;
4250 struct banData *ban, **bans;
4254 else if(strchr(search = argv[1], '!'))
4257 search_wilds = search[strcspn(search, "?*")];
4259 else if(!(search_u = GetUserH(search)))
4260 reply("MSG_NICK_UNKNOWN", search);
4262 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4264 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4268 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4273 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4276 bans[matches++] = ban;
4281 tbl.length = matches + 1;
4282 tbl.width = 4 + timed;
4284 tbl.flags = TABLE_NO_FREE;
4285 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4286 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4287 tbl.contents[0][0] = "Mask";
4288 tbl.contents[0][1] = "Set By";
4289 tbl.contents[0][2] = "Triggered";
4292 tbl.contents[0][3] = "Expires";
4293 tbl.contents[0][4] = "Reason";
4296 tbl.contents[0][3] = "Reason";
4299 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4301 free(tbl.contents[0]);
4306 msg_never = user_find_message(user, "MSG_NEVER");
4307 for(ii = 0; ii < matches; )
4313 else if(ban->expires)
4314 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4316 expires = msg_never;
4319 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4321 triggered = msg_never;
4323 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4324 tbl.contents[ii][0] = ban->mask;
4325 tbl.contents[ii][1] = ban->owner;
4326 tbl.contents[ii][2] = strdup(triggered);
4329 tbl.contents[ii][3] = strdup(expires);
4330 tbl.contents[ii][4] = ban->reason;
4333 tbl.contents[ii][3] = ban->reason;
4335 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4336 reply("MSG_MATCH_COUNT", matches);
4337 for(ii = 1; ii < tbl.length; ++ii)
4339 free((char*)tbl.contents[ii][2]);
4341 free((char*)tbl.contents[ii][3]);
4342 free(tbl.contents[ii]);
4344 free(tbl.contents[0]);
4350 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4352 struct chanData *cData = channel->channel_info;
4353 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4355 if(cData->topic_mask)
4357 if(cData->flags & CHANNEL_ADVTOPIC)
4359 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4360 char topicmask[TOPICLEN];
4361 int skipnum, topicpos = 0;
4362 char *ptr = cData->topic_mask;
4363 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4366 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4368 topicmask[topicpos++] = '*';
4370 topicmask[topicpos++] = *ptr;
4373 topicmask[topicpos++] = *ptr;
4377 topicmask[topicpos] = 0;
4378 return !match_ircglob(new_topic, topicmask);
4381 return !match_ircglob(new_topic, cData->topic_mask);
4383 else if(cData->topic)
4384 return irccasecmp(new_topic, cData->topic);
4389 static CHANSERV_FUNC(cmd_topic)
4391 struct chanData *cData;
4394 cData = channel->channel_info;
4399 SetChannelTopic(channel, chanserv, cData->topic, 1);
4400 reply("CSMSG_TOPIC_SET", cData->topic);
4404 reply("CSMSG_NO_TOPIC", channel->name);
4408 topic = unsplit_string(argv + 1, argc - 1, NULL);
4409 /* If they say "!topic *", use an empty topic. */
4410 if((topic[0] == '*') && (topic[1] == 0))
4412 if(bad_topic(channel, user, topic))
4414 char *topic_mask = cData->topic_mask;
4417 char new_topic[TOPICLEN+1], tchar;
4418 int pos=0, starpos=-1, dpos=0, len;
4420 if(cData->flags & CHANNEL_ADVTOPIC)
4422 //first check if there is a leading 'modifier id'
4423 int advtopic_index = 0;
4426 for(; topic[pos]; pos++)
4428 if(topic[pos] == ' ')
4430 //leading number found, cut off and store value in advtopic_index
4432 advtopic_index = atoi(topic) - 1; //no zerobase
4433 topic = &topic[pos+1];
4434 /* If they say "!topic 2 *", unset advtopic id 2. */
4435 if((topic[0] == '*') && (topic[1] == 0))
4438 if(!isdigit(topic[pos]))
4441 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4444 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4447 if(cData->advtopic[advtopic_index])
4448 free(cData->advtopic[advtopic_index]);
4449 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4450 char *ptr = topic_mask;
4451 while(*ptr && (dpos <= TOPICLEN))
4457 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4458 numbuf[numpos++] = *ptr;
4461 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4463 new_topic[dpos++] = *ptr; //is % again
4466 advtopic_index--; //no zero base
4467 if(!cData->advtopic[advtopic_index])
4468 break; //just leave it empty
4469 len = strlen(cData->advtopic[advtopic_index]);
4470 if((dpos + len) > TOPICLEN)
4471 len = TOPICLEN + 1 - dpos;
4472 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4476 ptr++; /* and fall through */
4479 new_topic[dpos++] = *ptr;
4485 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4492 len = strlen(topic);
4493 if((dpos + len) > TOPICLEN)
4494 len = TOPICLEN + 1 - dpos;
4495 memcpy(new_topic+dpos, topic, len);
4499 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4500 default: new_topic[dpos++] = tchar; break;
4503 if((dpos > TOPICLEN) || tchar)
4506 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4507 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4511 new_topic[dpos] = 0;
4512 SetChannelTopic(channel, chanserv, new_topic, 1);
4514 reply("CSMSG_TOPIC_LOCKED", channel->name);
4519 SetChannelTopic(channel, chanserv, topic, 1);
4521 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4523 /* Grab the topic and save it as the default topic. */
4525 cData->topic = strdup(channel->topic);
4531 static CHANSERV_FUNC(cmd_mode)
4533 struct userData *uData;
4534 struct mod_chanmode *change;
4540 change = &channel->channel_info->modes;
4541 if(change->modes_set || change->modes_clear) {
4542 modcmd_chanmode_announce(change);
4543 reply("CSMSG_DEFAULTED_MODES", channel->name);
4545 reply("CSMSG_NO_MODES", channel->name);
4549 uData = GetChannelUser(channel->channel_info, user->handle_info);
4551 base_oplevel = MAXOPLEVEL;
4552 else if (uData->access >= UL_OWNER)
4555 base_oplevel = 1 + UL_OWNER - uData->access;
4556 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4559 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4563 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4564 && mode_lock_violated(&channel->channel_info->modes, change))
4567 mod_chanmode_format(&channel->channel_info->modes, modes);
4568 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4572 modcmd_chanmode_announce(change);
4573 mod_chanmode_format(change, fmt);
4574 mod_chanmode_free(change);
4575 reply("CSMSG_MODES_SET", fmt);
4580 chanserv_del_invite_mark(void *data)
4582 struct ChanUser *chanuser = data;
4583 struct chanNode *channel = chanuser->chan;
4585 if(!channel) return;
4586 for(i = 0; i < channel->invited.used; i++)
4588 if(channel->invited.list[i] == chanuser->user) {
4589 userList_remove(&channel->invited, chanuser->user);
4595 static CHANSERV_FUNC(cmd_invite)
4597 struct userNode *invite;
4598 struct ChanUser *chanuser;
4603 if(!(invite = GetUserH(argv[1])))
4605 reply("MSG_NICK_UNKNOWN", argv[1]);
4612 if(GetUserMode(channel, invite))
4614 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4618 for(i = 0; i < channel->invited.used; i++)
4620 if(channel->invited.list[i] == invite) {
4621 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4630 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4631 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4634 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4636 irc_invite(chanserv, invite, channel);
4638 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4640 userList_append(&channel->invited, invite);
4641 chanuser = calloc(1, sizeof(*chanuser));
4642 chanuser->user=invite;
4643 chanuser->chan=channel;
4644 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4649 static CHANSERV_FUNC(cmd_inviteme)
4651 if(GetUserMode(channel, user))
4653 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4656 if(channel->channel_info
4657 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4659 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4662 irc_invite(cmd->parent->bot, user, channel);
4666 static CHANSERV_FUNC(cmd_invitemeall)
4668 struct handle_info *target = user->handle_info;
4669 struct userData *uData;
4671 if(!target->channels)
4673 reply("CSMSG_SQUAT_ACCESS", target->handle);
4677 for(uData = target->channels; uData; uData = uData->u_next)
4679 struct chanData *cData = uData->channel;
4680 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4682 irc_invite(cmd->parent->bot, user, cData->channel);
4689 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4692 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4694 /* We display things based on two dimensions:
4695 * - Issue time: present or absent
4696 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4697 * (in order of precedence, so something both expired and revoked
4698 * only counts as revoked)
4700 combo = (suspended->issued ? 4 : 0)
4701 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4703 case 0: /* no issue time, indefinite expiration */
4704 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4706 case 1: /* no issue time, expires in future */
4707 intervalString(buf1, suspended->expires-now, user->handle_info);
4708 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4710 case 2: /* no issue time, expired */
4711 intervalString(buf1, now-suspended->expires, user->handle_info);
4712 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4714 case 3: /* no issue time, revoked */
4715 intervalString(buf1, now-suspended->revoked, user->handle_info);
4716 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4718 case 4: /* issue time set, indefinite expiration */
4719 intervalString(buf1, now-suspended->issued, user->handle_info);
4720 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4722 case 5: /* issue time set, expires in future */
4723 intervalString(buf1, now-suspended->issued, user->handle_info);
4724 intervalString(buf2, suspended->expires-now, user->handle_info);
4725 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4727 case 6: /* issue time set, expired */
4728 intervalString(buf1, now-suspended->issued, user->handle_info);
4729 intervalString(buf2, now-suspended->expires, user->handle_info);
4730 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4732 case 7: /* issue time set, revoked */
4733 intervalString(buf1, now-suspended->issued, user->handle_info);
4734 intervalString(buf2, now-suspended->revoked, user->handle_info);
4735 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4738 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4744 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4747 const char *fmt = "%a %b %d %H:%M %Y";
4748 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4750 if(giveownership->staff_issuer)
4752 if(giveownership->reason)
4753 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4754 giveownership->target, giveownership->target_access,
4755 giveownership->staff_issuer, buf, giveownership->reason);
4757 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4758 giveownership->target, giveownership->target_access,
4759 giveownership->staff_issuer, buf);
4763 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4767 static CHANSERV_FUNC(cmd_info)
4769 char modes[MAXLEN], buffer[INTERVALLEN];
4770 struct userData *uData, *owner;
4771 struct chanData *cData;
4772 struct do_not_register *dnr;
4777 cData = channel->channel_info;
4778 reply("CSMSG_CHANNEL_INFO", channel->name);
4780 uData = GetChannelUser(cData, user->handle_info);
4781 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4783 mod_chanmode_format(&cData->modes, modes);
4784 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4785 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4788 for(it = dict_first(cData->notes); it; it = iter_next(it))
4792 note = iter_data(it);
4793 if(!note_type_visible_to_user(cData, note->type, user))
4796 padding = PADLEN - 1 - strlen(iter_key(it));
4797 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4800 if(cData->max_time) {
4801 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4803 reply("CSMSG_CHANNEL_MAX", cData->max);
4805 for(owner = cData->users; owner; owner = owner->next)
4806 if(owner->access == UL_OWNER)
4807 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4808 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4809 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4810 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4812 privileged = IsStaff(user);
4814 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4815 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4816 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4818 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4819 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4821 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4823 struct suspended *suspended;
4824 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4825 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4826 show_suspension_info(cmd, user, suspended);
4828 else if(IsSuspended(cData))
4830 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4831 show_suspension_info(cmd, user, cData->suspended);
4834 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4836 struct giveownership *giveownership;
4837 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4838 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4839 show_giveownership_info(cmd, user, giveownership);
4844 static CHANSERV_FUNC(cmd_netinfo)
4846 extern unsigned long boot_time;
4847 extern unsigned long burst_length;
4848 char interval[INTERVALLEN];
4850 reply("CSMSG_NETWORK_INFO");
4851 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4852 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4853 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4854 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4855 reply("CSMSG_NETWORK_BANS", banCount);
4856 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4857 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4858 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4863 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4865 struct helpfile_table table;
4867 struct userNode *user;
4872 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4873 table.contents = alloca(list->used*sizeof(*table.contents));
4874 for(nn=0; nn<list->used; nn++)
4876 user = list->list[nn];
4877 if(user->modes & skip_flags)
4883 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4886 nick = alloca(strlen(user->nick)+3);
4887 sprintf(nick, "(%s)", user->nick);
4891 table.contents[table.length][0] = nick;
4894 table_send(chanserv, to->nick, 0, NULL, table);
4897 static CHANSERV_FUNC(cmd_ircops)
4899 reply("CSMSG_STAFF_OPERS");
4900 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4904 static CHANSERV_FUNC(cmd_helpers)
4906 reply("CSMSG_STAFF_HELPERS");
4907 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4911 static CHANSERV_FUNC(cmd_staff)
4913 reply("CSMSG_NETWORK_STAFF");
4914 cmd_ircops(CSFUNC_ARGS);
4915 cmd_helpers(CSFUNC_ARGS);
4919 static CHANSERV_FUNC(cmd_peek)
4921 struct modeNode *mn;
4922 char modes[MODELEN];
4924 struct helpfile_table table;
4925 int opcount = 0, voicecount = 0, srvcount = 0;
4927 irc_make_chanmode(channel, modes);
4929 reply("CSMSG_PEEK_INFO", channel->name);
4930 reply("CSMSG_PEEK_TOPIC", channel->topic);
4931 reply("CSMSG_PEEK_MODES", modes);
4935 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4936 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4937 for(n = 0; n < channel->members.used; n++)
4939 mn = channel->members.list[n];
4940 if(IsLocal(mn->user))
4942 else if(mn->modes & MODE_CHANOP)
4944 else if(mn->modes & MODE_VOICE)
4947 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4949 table.contents[table.length] = alloca(sizeof(**table.contents));
4950 table.contents[table.length][0] = mn->user->nick;
4954 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4955 (channel->members.used - opcount - voicecount - srvcount));
4959 reply("CSMSG_PEEK_OPS");
4960 table_send(chanserv, user->nick, 0, NULL, table);
4963 reply("CSMSG_PEEK_NO_OPS");
4967 static MODCMD_FUNC(cmd_wipeinfo)
4969 struct handle_info *victim;
4970 struct userData *ud, *actor, *real_actor;
4971 unsigned int override = 0;
4974 actor = GetChannelUser(channel->channel_info, user->handle_info);
4975 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4976 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4978 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4980 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4983 if((ud->access >= actor->access) && (ud != actor))
4985 reply("MSG_USER_OUTRANKED", victim->handle);
4988 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4989 override = CMD_LOG_OVERRIDE;
4993 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4994 return 1 | override;
4997 static CHANSERV_FUNC(cmd_resync)
4999 struct mod_chanmode *changes;
5000 struct chanData *cData = channel->channel_info;
5001 unsigned int ii, used;
5003 changes = mod_chanmode_alloc(channel->members.used * 2);
5004 for(ii = used = 0; ii < channel->members.used; ++ii)
5006 struct modeNode *mn = channel->members.list[ii];
5007 struct userData *uData;
5009 if(IsService(mn->user))
5012 uData = GetChannelAccess(cData, mn->user->handle_info);
5013 if(!cData->lvlOpts[lvlGiveOps]
5014 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
5016 if(!(mn->modes & MODE_CHANOP))
5018 if(!uData || IsUserAutoOp(uData))
5020 changes->args[used].mode = MODE_CHANOP;
5021 changes->args[used++].u.member = mn;
5022 if(!(mn->modes & MODE_VOICE))
5024 changes->args[used].mode = MODE_VOICE;
5025 changes->args[used++].u.member = mn;
5030 else if(!cData->lvlOpts[lvlGiveVoice]
5031 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5033 if(mn->modes & MODE_CHANOP)
5035 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5036 changes->args[used++].u.member = mn;
5038 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5040 changes->args[used].mode = MODE_VOICE;
5041 changes->args[used++].u.member = mn;
5048 changes->args[used].mode = MODE_REMOVE | mn->modes;
5049 changes->args[used++].u.member = mn;
5053 changes->argc = used;
5054 modcmd_chanmode_announce(changes);
5055 mod_chanmode_free(changes);
5056 reply("CSMSG_RESYNCED_USERS", channel->name);
5060 static CHANSERV_FUNC(cmd_seen)
5062 struct userData *uData;
5063 struct handle_info *handle;
5064 char seen[INTERVALLEN];
5068 if(!irccasecmp(argv[1], chanserv->nick))
5070 reply("CSMSG_IS_CHANSERV");
5074 if(!(handle = get_handle_info(argv[1])))
5076 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5080 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5082 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5087 reply("CSMSG_USER_PRESENT", handle->handle);
5088 else if(uData->seen)
5089 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5091 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5093 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5094 reply("CSMSG_USER_VACATION", handle->handle);
5099 static MODCMD_FUNC(cmd_names)
5101 struct userNode *targ;
5102 struct userData *targData;
5103 unsigned int ii, pos;
5106 for(ii=pos=0; ii<channel->members.used; ++ii)
5108 targ = channel->members.list[ii]->user;
5109 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5112 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5115 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5119 if(IsUserSuspended(targData))
5121 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5124 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5125 reply("CSMSG_END_NAMES", channel->name);
5130 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5132 switch(ntype->visible_type)
5134 case NOTE_VIS_ALL: return 1;
5135 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5136 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5141 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5143 struct userData *uData;
5145 switch(ntype->set_access_type)
5147 case NOTE_SET_CHANNEL_ACCESS:
5148 if(!user->handle_info)
5150 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5152 return uData->access >= ntype->set_access.min_ulevel;
5153 case NOTE_SET_CHANNEL_SETTER:
5154 return check_user_level(channel, user, lvlSetters, 1, 0);
5155 case NOTE_SET_PRIVILEGED: default:
5156 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5160 static CHANSERV_FUNC(cmd_note)
5162 struct chanData *cData;
5164 struct note_type *ntype;
5166 cData = channel->channel_info;
5169 reply("CSMSG_NOT_REGISTERED", channel->name);
5173 /* If no arguments, show all visible notes for the channel. */
5179 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5181 note = iter_data(it);
5182 if(!note_type_visible_to_user(cData, note->type, user))
5185 reply("CSMSG_NOTELIST_HEADER", channel->name);
5186 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5189 reply("CSMSG_NOTELIST_END", channel->name);
5191 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5193 /* If one argument, show the named note. */
5196 if((note = dict_find(cData->notes, argv[1], NULL))
5197 && note_type_visible_to_user(cData, note->type, user))
5199 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5201 else if((ntype = dict_find(note_types, argv[1], NULL))
5202 && note_type_visible_to_user(NULL, ntype, user))
5204 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5209 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5213 /* Assume they're trying to set a note. */
5217 ntype = dict_find(note_types, argv[1], NULL);
5220 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5223 else if(note_type_settable_by_user(channel, ntype, user))
5225 note_text = unsplit_string(argv+2, argc-2, NULL);
5226 if((note = dict_find(cData->notes, argv[1], NULL)))
5227 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5228 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5229 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5231 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5233 /* The note is viewable to staff only, so return 0
5234 to keep the invocation from getting logged (or
5235 regular users can see it in !events). */
5241 reply("CSMSG_NO_ACCESS");
5248 static CHANSERV_FUNC(cmd_delnote)
5253 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5254 || !note_type_settable_by_user(channel, note->type, user))
5256 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5259 dict_remove(channel->channel_info->notes, note->type->name);
5260 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5264 static CHANSERV_FUNC(cmd_events)
5266 struct logSearch discrim;
5267 struct logReport report;
5268 unsigned int matches, limit;
5270 limit = (argc > 1) ? atoi(argv[1]) : 10;
5271 if(limit < 1 || limit > 200)
5274 memset(&discrim, 0, sizeof(discrim));
5275 discrim.masks.bot = chanserv;
5276 discrim.masks.channel_name = channel->name;
5278 discrim.masks.command = argv[2];
5279 discrim.limit = limit;
5280 discrim.max_time = INT_MAX;
5281 discrim.severities = 1 << LOG_COMMAND;
5282 report.reporter = chanserv;
5284 reply("CSMSG_EVENT_SEARCH_RESULTS");
5285 matches = log_entry_search(&discrim, log_report_entry, &report);
5287 reply("MSG_MATCH_COUNT", matches);
5289 reply("MSG_NO_MATCHES");
5293 static CHANSERV_FUNC(cmd_say)
5299 msg = unsplit_string(argv + 1, argc - 1, NULL);
5300 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5302 else if(*argv[1] == '*' && argv[1][1] != '\0')
5304 struct handle_info *hi;
5305 struct userNode *authed;
5308 msg = unsplit_string(argv + 2, argc - 2, NULL);
5310 if (!(hi = get_handle_info(argv[1] + 1)))
5312 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5316 for (authed = hi->users; authed; authed = authed->next_authed)
5317 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5319 else if(GetUserH(argv[1]))
5322 msg = unsplit_string(argv + 2, argc - 2, NULL);
5323 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5327 reply("MSG_NOT_TARGET_NAME");
5333 static CHANSERV_FUNC(cmd_emote)
5339 /* CTCP is so annoying. */
5340 msg = unsplit_string(argv + 1, argc - 1, NULL);
5341 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5343 else if(*argv[1] == '*' && argv[1][1] != '\0')
5345 struct handle_info *hi;
5346 struct userNode *authed;
5349 msg = unsplit_string(argv + 2, argc - 2, NULL);
5351 if (!(hi = get_handle_info(argv[1] + 1)))
5353 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5357 for (authed = hi->users; authed; authed = authed->next_authed)
5358 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5360 else if(GetUserH(argv[1]))
5362 msg = unsplit_string(argv + 2, argc - 2, NULL);
5363 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5367 reply("MSG_NOT_TARGET_NAME");
5373 struct channelList *
5374 chanserv_support_channels(void)
5376 return &chanserv_conf.support_channels;
5379 static CHANSERV_FUNC(cmd_expire)
5381 int channel_count = registered_channels;
5382 expire_channels(chanserv);
5383 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5388 chanserv_expire_suspension(void *data)
5390 struct suspended *suspended = data;
5391 struct chanNode *channel;
5394 /* Update the channel registration data structure. */
5395 if(!suspended->expires || (now < suspended->expires))
5396 suspended->revoked = now;
5397 channel = suspended->cData->channel;
5398 suspended->cData->channel = channel;
5399 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5401 /* If appropriate, re-join ChanServ to the channel. */
5402 if(!IsOffChannel(suspended->cData))
5404 spamserv_cs_suspend(channel, 0, 0, NULL);
5405 ss_cs_join_channel(channel, 1);
5408 /* Mark everyone currently in the channel as present. */
5409 for(ii = 0; ii < channel->members.used; ++ii)
5411 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5420 static CHANSERV_FUNC(cmd_csuspend)
5422 struct suspended *suspended;
5423 char reason[MAXLEN];
5424 unsigned long expiry, duration;
5425 struct userData *uData;
5429 if(IsProtected(channel->channel_info))
5431 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5435 if(argv[1][0] == '!')
5437 else if(IsSuspended(channel->channel_info))
5439 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5440 show_suspension_info(cmd, user, channel->channel_info->suspended);
5444 if(!strcmp(argv[1], "0"))
5446 else if((duration = ParseInterval(argv[1])))
5447 expiry = now + duration;
5450 reply("MSG_INVALID_DURATION", argv[1]);
5454 unsplit_string(argv + 2, argc - 2, reason);
5456 suspended = calloc(1, sizeof(*suspended));
5457 suspended->revoked = 0;
5458 suspended->issued = now;
5459 suspended->suspender = strdup(user->handle_info->handle);
5460 suspended->expires = expiry;
5461 suspended->reason = strdup(reason);
5462 suspended->cData = channel->channel_info;
5463 suspended->previous = suspended->cData->suspended;
5464 suspended->cData->suspended = suspended;
5466 if(suspended->expires)
5467 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5469 if(IsSuspended(channel->channel_info))
5471 suspended->previous->revoked = now;
5472 if(suspended->previous->expires)
5473 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5474 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5475 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5479 /* Mark all users in channel as absent. */
5480 for(uData = channel->channel_info->users; uData; uData = uData->next)
5489 /* Mark the channel as suspended, then part. */
5490 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5491 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5492 DelChannelUser(chanserv, channel, suspended->reason, 0);
5493 reply("CSMSG_SUSPENDED", channel->name);
5494 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5495 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5500 static CHANSERV_FUNC(cmd_cunsuspend)
5502 struct suspended *suspended;
5503 char message[MAXLEN];
5505 if(!IsSuspended(channel->channel_info))
5507 reply("CSMSG_NOT_SUSPENDED", channel->name);
5511 suspended = channel->channel_info->suspended;
5513 /* Expire the suspension and join ChanServ to the channel. */
5514 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5515 chanserv_expire_suspension(suspended);
5516 reply("CSMSG_UNSUSPENDED", channel->name);
5517 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5518 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5522 typedef struct chanservSearch
5527 unsigned long unvisited;
5528 unsigned long registered;
5530 unsigned long flags;
5534 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5537 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5542 search = malloc(sizeof(struct chanservSearch));
5543 memset(search, 0, sizeof(*search));
5546 for(i = 0; i < argc; i++)
5548 /* Assume all criteria require arguments. */
5551 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5555 if(!irccasecmp(argv[i], "name"))
5556 search->name = argv[++i];
5557 else if(!irccasecmp(argv[i], "registrar"))
5558 search->registrar = argv[++i];
5559 else if(!irccasecmp(argv[i], "unvisited"))
5560 search->unvisited = ParseInterval(argv[++i]);
5561 else if(!irccasecmp(argv[i], "registered"))
5562 search->registered = ParseInterval(argv[++i]);
5563 else if(!irccasecmp(argv[i], "flags"))
5566 if(!irccasecmp(argv[i], "nodelete"))
5567 search->flags |= CHANNEL_NODELETE;
5568 else if(!irccasecmp(argv[i], "suspended"))
5569 search->flags |= CHANNEL_SUSPENDED;
5570 else if(!irccasecmp(argv[i], "unreviewed"))
5571 search->flags |= CHANNEL_UNREVIEWED;
5574 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5578 else if(!irccasecmp(argv[i], "limit"))
5579 search->limit = strtoul(argv[++i], NULL, 10);
5582 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5587 if(search->name && !strcmp(search->name, "*"))
5589 if(search->registrar && !strcmp(search->registrar, "*"))
5590 search->registrar = 0;
5599 chanserv_channel_match(struct chanData *channel, search_t search)
5601 const char *name = channel->channel->name;
5602 if((search->name && !match_ircglob(name, search->name)) ||
5603 (search->registrar && !channel->registrar) ||
5604 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5605 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5606 (search->registered && (now - channel->registered) > search->registered) ||
5607 (search->flags && ((search->flags & channel->flags) != search->flags)))
5614 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5616 struct chanData *channel;
5617 unsigned int matches = 0;
5619 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5621 if(!chanserv_channel_match(channel, search))
5631 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5636 search_print(struct chanData *channel, void *data)
5638 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5641 static CHANSERV_FUNC(cmd_search)
5644 unsigned int matches;
5645 channel_search_func action;
5649 if(!irccasecmp(argv[1], "count"))
5650 action = search_count;
5651 else if(!irccasecmp(argv[1], "print"))
5652 action = search_print;
5655 reply("CSMSG_ACTION_INVALID", argv[1]);
5659 search = chanserv_search_create(user, argc - 2, argv + 2);
5663 if(action == search_count)
5664 search->limit = INT_MAX;
5666 if(action == search_print)
5667 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5669 matches = chanserv_channel_search(search, action, user);
5672 reply("MSG_MATCH_COUNT", matches);
5674 reply("MSG_NO_MATCHES");
5680 static CHANSERV_FUNC(cmd_unvisited)
5682 struct chanData *cData;
5683 unsigned long interval = chanserv_conf.channel_expire_delay;
5684 char buffer[INTERVALLEN];
5685 unsigned int limit = 25, matches = 0;
5689 interval = ParseInterval(argv[1]);
5691 limit = atoi(argv[2]);
5694 intervalString(buffer, interval, user->handle_info);
5695 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5697 for(cData = channelList; cData && matches < limit; cData = cData->next)
5699 if((now - cData->visited) < interval)
5702 intervalString(buffer, now - cData->visited, user->handle_info);
5703 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5710 static MODCMD_FUNC(chan_opt_defaulttopic)
5716 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5718 reply("CSMSG_TOPIC_LOCKED", channel->name);
5722 topic = unsplit_string(argv+1, argc-1, NULL);
5724 free(channel->channel_info->topic);
5725 if(topic[0] == '*' && topic[1] == 0)
5727 topic = channel->channel_info->topic = NULL;
5731 topic = channel->channel_info->topic = strdup(topic);
5732 if(channel->channel_info->topic_mask
5733 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5734 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5736 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5739 if(channel->channel_info->topic)
5740 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5742 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5746 static MODCMD_FUNC(chan_opt_topicmask)
5750 struct chanData *cData = channel->channel_info;
5753 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5755 reply("CSMSG_TOPIC_LOCKED", channel->name);
5759 mask = unsplit_string(argv+1, argc-1, NULL);
5761 if(cData->topic_mask)
5762 free(cData->topic_mask);
5763 if(mask[0] == '*' && mask[1] == 0)
5765 cData->topic_mask = 0;
5769 cData->topic_mask = strdup(mask);
5771 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5772 else if(!match_ircglob(cData->topic, cData->topic_mask))
5773 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5777 if(channel->channel_info->topic_mask)
5778 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5780 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5784 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5788 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5792 if(greeting[0] == '*' && greeting[1] == 0)
5796 unsigned int length = strlen(greeting);
5797 if(length > chanserv_conf.greeting_length)
5799 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5802 *data = strdup(greeting);
5811 reply(name, user_find_message(user, "MSG_NONE"));
5815 static MODCMD_FUNC(chan_opt_greeting)
5817 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5820 static MODCMD_FUNC(chan_opt_usergreeting)
5822 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5825 static MODCMD_FUNC(chan_opt_modes)
5827 struct mod_chanmode *new_modes;
5832 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5834 reply("CSMSG_NO_ACCESS");
5837 if(argv[1][0] == '*' && argv[1][1] == 0)
5839 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5841 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)))
5843 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5846 else if(new_modes->argc > 1)
5848 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5849 mod_chanmode_free(new_modes);
5854 channel->channel_info->modes = *new_modes;
5855 modcmd_chanmode_announce(new_modes);
5856 mod_chanmode_free(new_modes);
5860 mod_chanmode_format(&channel->channel_info->modes, modes);
5862 reply("CSMSG_SET_MODES", modes);
5864 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5868 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5870 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5872 struct chanData *cData = channel->channel_info;
5877 /* Set flag according to value. */
5878 if(enabled_string(argv[1]))
5880 cData->flags |= mask;
5883 else if(disabled_string(argv[1]))
5885 cData->flags &= ~mask;
5890 reply("MSG_INVALID_BINARY", argv[1]);
5896 /* Find current option value. */
5897 value = (cData->flags & mask) ? 1 : 0;
5901 reply(name, user_find_message(user, "MSG_ON"));
5903 reply(name, user_find_message(user, "MSG_OFF"));
5907 static MODCMD_FUNC(chan_opt_nodelete)
5909 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5911 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5915 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5918 static MODCMD_FUNC(chan_opt_dynlimit)
5920 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5923 static MODCMD_FUNC(chan_opt_advtopic)
5925 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5928 static MODCMD_FUNC(chan_opt_offchannel)
5930 struct chanData *cData = channel->channel_info;
5935 /* Set flag according to value. */
5936 if(enabled_string(argv[1]))
5938 if(!IsOffChannel(cData))
5939 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5940 cData->flags |= CHANNEL_OFFCHANNEL;
5943 else if(disabled_string(argv[1]))
5945 if(IsOffChannel(cData))
5947 struct mod_chanmode change;
5948 mod_chanmode_init(&change);
5950 change.args[0].mode = MODE_CHANOP;
5951 change.args[0].u.member = AddChannelUser(chanserv, channel);
5952 mod_chanmode_announce(chanserv, channel, &change);
5954 cData->flags &= ~CHANNEL_OFFCHANNEL;
5959 reply("MSG_INVALID_BINARY", argv[1]);
5965 /* Find current option value. */
5966 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5970 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5972 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5976 static MODCMD_FUNC(chan_opt_unreviewed)
5978 struct chanData *cData = channel->channel_info;
5979 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5985 /* The two directions can have different ACLs. */
5986 if(enabled_string(argv[1]))
5988 else if(disabled_string(argv[1]))
5992 reply("MSG_INVALID_BINARY", argv[1]);
5996 if (new_value != value)
5998 struct svccmd *subcmd;
5999 char subcmd_name[32];
6001 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
6002 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
6005 reply("MSG_COMMAND_DISABLED", subcmd_name);
6008 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6012 cData->flags |= CHANNEL_UNREVIEWED;
6015 free(cData->registrar);
6016 cData->registrar = strdup(user->handle_info->handle);
6017 cData->flags &= ~CHANNEL_UNREVIEWED;
6024 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6026 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6030 static MODCMD_FUNC(chan_opt_defaults)
6032 struct userData *uData;
6033 struct chanData *cData;
6034 const char *confirm;
6035 enum levelOption lvlOpt;
6036 enum charOption chOpt;
6038 cData = channel->channel_info;
6039 uData = GetChannelUser(cData, user->handle_info);
6040 if(!uData || (uData->access < UL_OWNER))
6042 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6045 confirm = make_confirmation_string(uData);
6046 if((argc < 2) || strcmp(argv[1], confirm))
6048 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6051 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6052 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6053 cData->modes = chanserv_conf.default_modes;
6054 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6055 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6056 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6057 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6058 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6063 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6065 struct chanData *cData = channel->channel_info;
6066 struct userData *uData;
6067 unsigned short value;
6071 if(!check_user_level(channel, user, option, 1, 1))
6073 reply("CSMSG_CANNOT_SET");
6076 value = user_level_from_name(argv[1], UL_OWNER+1);
6077 if(!value && strcmp(argv[1], "0"))
6079 reply("CSMSG_INVALID_ACCESS", argv[1]);
6082 uData = GetChannelUser(cData, user->handle_info);
6083 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6085 reply("CSMSG_BAD_SETLEVEL");
6091 if(value > cData->lvlOpts[lvlGiveOps])
6093 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6098 if(value < cData->lvlOpts[lvlGiveVoice])
6100 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6105 /* This test only applies to owners, since non-owners
6106 * trying to set an option to above their level get caught
6107 * by the CSMSG_BAD_SETLEVEL test above.
6109 if(value > uData->access)
6111 reply("CSMSG_BAD_SETTERS");
6118 cData->lvlOpts[option] = value;
6120 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6124 static MODCMD_FUNC(chan_opt_enfops)
6126 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6129 static MODCMD_FUNC(chan_opt_giveops)
6131 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6134 static MODCMD_FUNC(chan_opt_enfmodes)
6136 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6139 static MODCMD_FUNC(chan_opt_enftopic)
6141 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6144 static MODCMD_FUNC(chan_opt_pubcmd)
6146 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6149 static MODCMD_FUNC(chan_opt_setters)
6151 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6154 static MODCMD_FUNC(chan_opt_ctcpusers)
6156 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6159 static MODCMD_FUNC(chan_opt_userinfo)
6161 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6164 static MODCMD_FUNC(chan_opt_givevoice)
6166 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6169 static MODCMD_FUNC(chan_opt_topicsnarf)
6171 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6174 static MODCMD_FUNC(chan_opt_vote)
6176 return channel_level_option(lvlVote, CSFUNC_ARGS);
6179 static MODCMD_FUNC(chan_opt_inviteme)
6181 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6185 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6187 struct chanData *cData = channel->channel_info;
6188 int count = charOptions[option].count, idx;
6192 idx = atoi(argv[1]);
6194 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6196 reply("CSMSG_INVALID_NUMERIC", idx);
6197 /* Show possible values. */
6198 for(idx = 0; idx < count; idx++)
6199 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6203 cData->chOpts[option] = charOptions[option].values[idx].value;
6207 /* Find current option value. */
6210 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6214 /* Somehow, the option value is corrupt; reset it to the default. */
6215 cData->chOpts[option] = charOptions[option].default_value;
6220 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6224 static MODCMD_FUNC(chan_opt_protect)
6226 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6229 static MODCMD_FUNC(chan_opt_toys)
6231 return channel_multiple_option(chToys, CSFUNC_ARGS);
6234 static MODCMD_FUNC(chan_opt_ctcpreaction)
6236 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6239 static MODCMD_FUNC(chan_opt_topicrefresh)
6241 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6244 static struct svccmd_list set_shows_list;
6247 handle_svccmd_unbind(struct svccmd *target) {
6249 for(ii=0; ii<set_shows_list.used; ++ii)
6250 if(target == set_shows_list.list[ii])
6251 set_shows_list.used = 0;
6254 static CHANSERV_FUNC(cmd_set)
6256 struct svccmd *subcmd;
6260 /* Check if we need to (re-)initialize set_shows_list. */
6261 if(!set_shows_list.used)
6263 if(!set_shows_list.size)
6265 set_shows_list.size = chanserv_conf.set_shows->used;
6266 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6268 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6270 const char *name = chanserv_conf.set_shows->list[ii];
6271 sprintf(buf, "%s %s", argv[0], name);
6272 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6275 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6278 svccmd_list_append(&set_shows_list, subcmd);
6284 reply("CSMSG_CHANNEL_OPTIONS");
6285 for(ii = 0; ii < set_shows_list.used; ii++)
6287 subcmd = set_shows_list.list[ii];
6288 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6293 sprintf(buf, "%s %s", argv[0], argv[1]);
6294 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6297 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6300 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6302 reply("CSMSG_NO_ACCESS");
6308 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6312 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6314 struct userData *uData;
6316 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6319 reply("CSMSG_NOT_USER", channel->name);
6325 /* Just show current option value. */
6327 else if(enabled_string(argv[1]))
6329 uData->flags |= mask;
6331 else if(disabled_string(argv[1]))
6333 uData->flags &= ~mask;
6337 reply("MSG_INVALID_BINARY", argv[1]);
6341 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6345 static MODCMD_FUNC(user_opt_noautoop)
6347 struct userData *uData;
6349 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6352 reply("CSMSG_NOT_USER", channel->name);
6355 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6356 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6358 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6361 static MODCMD_FUNC(user_opt_autoinvite)
6363 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6365 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6367 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6370 static MODCMD_FUNC(user_opt_info)
6372 struct userData *uData;
6375 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6379 /* If they got past the command restrictions (which require access)
6380 * but fail this test, we have some fool with security override on.
6382 reply("CSMSG_NOT_USER", channel->name);
6389 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6390 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6392 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6395 bp = strcspn(infoline, "\001");
6398 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6403 if(infoline[0] == '*' && infoline[1] == 0)
6406 uData->info = strdup(infoline);
6409 reply("CSMSG_USET_INFO", uData->info);
6411 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6415 struct svccmd_list uset_shows_list;
6417 static CHANSERV_FUNC(cmd_uset)
6419 struct svccmd *subcmd;
6423 /* Check if we need to (re-)initialize uset_shows_list. */
6424 if(!uset_shows_list.used)
6428 "NoAutoOp", "AutoInvite", "Info"
6431 if(!uset_shows_list.size)
6433 uset_shows_list.size = ArrayLength(options);
6434 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6436 for(ii = 0; ii < ArrayLength(options); ii++)
6438 const char *name = options[ii];
6439 sprintf(buf, "%s %s", argv[0], name);
6440 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6443 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6446 svccmd_list_append(&uset_shows_list, subcmd);
6452 /* Do this so options are presented in a consistent order. */
6453 reply("CSMSG_USER_OPTIONS");
6454 for(ii = 0; ii < uset_shows_list.used; ii++)
6455 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6459 sprintf(buf, "%s %s", argv[0], argv[1]);
6460 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6463 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6467 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6470 static CHANSERV_FUNC(cmd_giveownership)
6472 struct handle_info *new_owner_hi;
6473 struct userData *new_owner;
6474 struct userData *curr_user;
6475 struct userData *invoker;
6476 struct chanData *cData = channel->channel_info;
6477 struct do_not_register *dnr;
6478 const char *confirm;
6479 struct giveownership *giveownership;
6480 unsigned int force, override;
6481 unsigned short co_access, new_owner_old_access;
6482 char reason[MAXLEN], transfer_reason[MAXLEN];
6485 curr_user = GetChannelAccess(cData, user->handle_info);
6486 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6488 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6489 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6490 && (uData->access > 500)
6491 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6492 || uData->access < 500));
6494 if(!curr_user || (curr_user->access != UL_OWNER))
6496 struct userData *owner = NULL;
6497 for(curr_user = channel->channel_info->users;
6499 curr_user = curr_user->next)
6501 if(curr_user->access != UL_OWNER)
6505 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6512 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6514 char delay[INTERVALLEN];
6515 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6516 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6519 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6521 if(new_owner_hi == user->handle_info)
6523 reply("CSMSG_NO_TRANSFER_SELF");
6526 new_owner = GetChannelAccess(cData, new_owner_hi);
6531 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6535 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6539 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6541 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6544 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6545 if(!IsHelping(user))
6546 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6548 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6551 invoker = GetChannelUser(cData, user->handle_info);
6552 if(invoker->access <= UL_OWNER)
6554 confirm = make_confirmation_string(curr_user);
6555 if((argc < 3) || strcmp(argv[2], confirm))
6557 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6561 new_owner_old_access = new_owner->access;
6562 if(new_owner->access >= UL_COOWNER)
6563 co_access = new_owner->access;
6565 co_access = UL_COOWNER;
6566 new_owner->access = UL_OWNER;
6568 curr_user->access = co_access;
6569 cData->ownerTransfer = now;
6570 giveownership = calloc(1, sizeof(*giveownership));
6571 giveownership->issued = now;
6572 giveownership->old_owner = curr_user->handle->handle;
6573 giveownership->target = new_owner_hi->handle;
6574 giveownership->target_access = new_owner_old_access;
6577 if(argc > (2 + force))
6579 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6580 giveownership->reason = strdup(transfer_reason);
6582 giveownership->staff_issuer = strdup(user->handle_info->handle);
6585 giveownership->previous = channel->channel_info->giveownership;
6586 channel->channel_info->giveownership = giveownership;
6587 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6588 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6589 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6594 chanserv_expire_user_suspension(void *data)
6596 struct userData *target = data;
6598 target->expires = 0;
6599 target->flags &= ~USER_SUSPENDED;
6602 static CHANSERV_FUNC(cmd_suspend)
6604 struct handle_info *hi;
6605 struct userData *actor, *real_actor, *target;
6606 unsigned int override = 0;
6610 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6611 actor = GetChannelUser(channel->channel_info, user->handle_info);
6612 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6613 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6615 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6618 if(target->access >= actor->access)
6620 reply("MSG_USER_OUTRANKED", hi->handle);
6623 if(target->flags & USER_SUSPENDED)
6625 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6630 target->present = 0;
6633 if(!real_actor || target->access >= real_actor->access)
6634 override = CMD_LOG_OVERRIDE;
6635 if(!strcmp(argv[2], "0"))
6639 unsigned int duration;
6640 if(!(duration = ParseInterval(argv[2])))
6642 reply("MSG_INVALID_DURATION", argv[2]);
6645 expiry = now + duration;
6648 target->expires = expiry;
6651 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6653 target->flags |= USER_SUSPENDED;
6654 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6655 return 1 | override;
6658 static CHANSERV_FUNC(cmd_unsuspend)
6660 struct handle_info *hi;
6661 struct userData *actor, *real_actor, *target;
6662 unsigned int override = 0;
6665 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6666 actor = GetChannelUser(channel->channel_info, user->handle_info);
6667 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6668 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6670 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6673 if(target->access >= actor->access)
6675 reply("MSG_USER_OUTRANKED", hi->handle);
6678 if(!(target->flags & USER_SUSPENDED))
6680 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6683 if(!real_actor || target->access >= real_actor->access)
6684 override = CMD_LOG_OVERRIDE;
6685 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
6686 target->flags &= ~USER_SUSPENDED;
6687 scan_user_presence(target, NULL);
6688 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6689 return 1 | override;
6692 static MODCMD_FUNC(cmd_deleteme)
6694 struct handle_info *hi;
6695 struct userData *target;
6696 const char *confirm_string;
6697 unsigned short access_level;
6700 hi = user->handle_info;
6701 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6703 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6706 if(target->access == UL_OWNER)
6708 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6711 confirm_string = make_confirmation_string(target);
6712 if((argc < 2) || strcmp(argv[1], confirm_string))
6714 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6717 access_level = target->access;
6718 channel_name = strdup(channel->name);
6719 del_channel_user(target, 1);
6720 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6725 static CHANSERV_FUNC(cmd_addvote)
6727 struct chanData *cData = channel->channel_info;
6728 struct userData *uData, *target;
6729 struct handle_info *hi;
6730 if (!cData) return 0;
6732 hi = user->handle_info;
6733 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6735 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6738 if(target->access < 300) {
6739 reply("CSMSG_NO_ACCESS");
6743 reply("CSMSG_ADDVOTE_FULL");
6747 msg = unsplit_string(argv + 1, argc - 1, NULL);
6748 cData->vote = strdup(msg);
6749 cData->vote_start=0;
6750 dict_delete(cData->vote_options);
6751 cData->vote_options = dict_new();
6752 dict_set_free_data(cData->vote_options, free_vote_options);
6753 for(uData = channel->channel_info->users; uData; uData = uData->next)
6758 reply("CSMSG_ADDVOTE_DONE");
6762 static CHANSERV_FUNC(cmd_delvote)
6764 struct chanData *cData = channel->channel_info;
6765 struct userData *target;
6766 struct handle_info *hi;
6767 if (!cData) return 0;
6768 hi = user->handle_info;
6769 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6771 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6774 if(target->access < 300) {
6775 reply("CSMSG_NO_ACCESS");
6779 reply("CSMSG_NO_VOTE");
6784 reply("CSMSG_DELVOTE_DONE");
6788 static CHANSERV_FUNC(cmd_addoption)
6790 struct chanData *cData = channel->channel_info;
6791 struct userData *target;
6792 struct handle_info *hi;
6793 if (!cData) return 0;
6795 hi = user->handle_info;
6796 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6798 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6801 if(target->access < 300) {
6802 reply("CSMSG_NO_ACCESS");
6806 reply("CSMSG_NO_VOTE");
6812 msg = unsplit_string(argv + 1, argc - 1, NULL);
6815 unsigned int lastid = 1;
6816 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6817 struct vote_option *cvOpt = iter_data(it);
6818 if(cvOpt->option_id > lastid)
6819 lastid = cvOpt->option_id;
6821 struct vote_option *vOpt;
6822 vOpt = calloc(1, sizeof(*vOpt));
6823 vOpt->name = strdup(msg);
6824 vOpt->option_id = (lastid + 1);
6826 sprintf(str,"%i",(lastid + 1));
6827 vOpt->option_str = strdup(str);
6829 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6831 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6835 static CHANSERV_FUNC(cmd_deloption)
6837 struct chanData *cData = channel->channel_info;
6838 struct userData *uData, *target;
6839 struct handle_info *hi;
6840 if (!cData) return 0;
6842 hi = user->handle_info;
6843 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6845 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6848 if(target->access < 300) {
6849 reply("CSMSG_NO_ACCESS");
6853 reply("CSMSG_NO_VOTE");
6856 if(cData->vote_start) {
6857 if(dict_size(cData->vote_options) < 3) {
6858 reply("CSMSG_VOTE_NEED_OPTIONS");
6863 int find_id = atoi(argv[1]);
6865 unsigned int found = 0;
6868 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6870 if (find_id == ii) {
6871 struct vote_option *vOpt = iter_data(it);
6872 found = vOpt->option_id;
6874 sprintf(str,"%i",vOpt->option_id);
6875 dict_remove(cData->vote_options, str);
6880 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6881 if(uData->votefor == found) {
6886 reply("CSMSG_DELOPTION_DONE");
6889 reply("CSMSG_DELOPTION_NONE");
6894 static CHANSERV_FUNC(cmd_vote)
6896 struct chanData *cData = channel->channel_info;
6897 struct userData *target;
6898 struct handle_info *hi;
6899 unsigned int votedfor = 0;
6900 char *votedfor_str = NULL;
6902 if (!cData || !cData->vote) {
6903 reply("CSMSG_NO_VOTE");
6906 if(argc > 1 && cData->vote_start) {
6907 hi = user->handle_info;
6908 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6910 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6913 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6914 reply("CSMSG_NO_ACCESS");
6918 reply("CSMSG_VOTE_VOTED");
6921 int find_id = atoi(argv[1]);
6924 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6926 if (find_id == ii) {
6927 struct vote_option *vOpt = iter_data(it);
6930 target->votefor = vOpt->option_id;
6931 votedfor = vOpt->option_id;
6932 votedfor_str = vOpt->name;
6936 reply("CSMSG_VOTE_INVALID");
6940 if (!cData->vote_start) {
6941 reply("CSMSG_VOTE_NOT_STARTED");
6943 reply("CSMSG_VOTE_QUESTION",cData->vote);
6945 unsigned int voteid = 0;
6948 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6949 struct vote_option *vOpt = iter_data(it);
6951 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6953 if(argc > 1 && cData->vote_start && votedfor_str) {
6954 reply("CSMSG_VOTE_DONE",votedfor_str);
6959 static CHANSERV_FUNC(cmd_startvote)
6961 struct chanData *cData = channel->channel_info;
6962 struct userData *target;
6963 struct handle_info *hi;
6964 if (!cData) return 0;
6965 hi = user->handle_info;
6966 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6968 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6971 if(target->access < 300) {
6972 reply("CSMSG_NO_ACCESS");
6976 reply("CSMSG_NO_VOTE");
6979 if(cData->vote_start) {
6980 reply("CSMSG_STARTVOTE_RUNNING");
6983 if(dict_size(cData->vote_options) < 2) {
6984 reply("CSMSG_VOTE_NEED_OPTIONS");
6987 cData->vote_start = 1;
6988 char response[MAXLEN];
6989 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6990 irc_privmsg(cmd->parent->bot, channel->name, response);
6991 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6992 irc_privmsg(cmd->parent->bot, channel->name, response);
6993 unsigned int voteid = 0;
6995 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6996 struct vote_option *vOpt = iter_data(it);
6998 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6999 irc_privmsg(cmd->parent->bot, channel->name, response);
7001 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
7002 irc_privmsg(cmd->parent->bot, channel->name, response);
7003 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
7004 irc_privmsg(cmd->parent->bot, channel->name, response);
7008 static CHANSERV_FUNC(cmd_endvote)
7010 struct chanData *cData = channel->channel_info;
7011 struct userData *target;
7012 struct handle_info *hi;
7013 if (!cData) return 0;
7014 hi = user->handle_info;
7015 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7017 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7020 if(target->access < 300) {
7021 reply("CSMSG_NO_ACCESS");
7025 reply("CSMSG_NO_VOTE");
7028 if(!cData->vote_start) {
7029 reply("CSMSG_ENDVOTE_STOPPED");
7032 cData->vote_start = 0;
7033 reply("CSMSG_ENDVOTE_DONE");
7037 static CHANSERV_FUNC(cmd_voteresults)
7039 struct chanData *cData = channel->channel_info;
7040 struct userData *target;
7041 struct handle_info *hi;
7042 if (!cData) return 0;
7044 reply("CSMSG_NO_VOTE");
7047 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7048 hi = user->handle_info;
7049 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7051 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7054 if(target->access < 300) {
7055 reply("CSMSG_NO_ACCESS");
7058 char response[MAXLEN];
7059 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7060 irc_privmsg(cmd->parent->bot, channel->name, response);
7061 unsigned int voteid = 0;
7063 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7064 struct vote_option *vOpt = iter_data(it);
7066 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7067 irc_privmsg(cmd->parent->bot, channel->name, response);
7070 reply("CSMSG_VOTE_QUESTION",cData->vote);
7071 unsigned int voteid = 0;
7073 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7074 struct vote_option *vOpt = iter_data(it);
7076 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7083 chanserv_refresh_topics(UNUSED_ARG(void *data))
7085 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7086 struct chanData *cData;
7089 for(cData = channelList; cData; cData = cData->next)
7091 if(IsSuspended(cData))
7093 opt = cData->chOpts[chTopicRefresh];
7096 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7099 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7100 cData->last_refresh = refresh_num;
7102 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7105 static CHANSERV_FUNC(cmd_unf)
7109 char response[MAXLEN];
7110 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7111 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7112 irc_privmsg(cmd->parent->bot, channel->name, response);
7115 reply("CSMSG_UNF_RESPONSE");
7119 static CHANSERV_FUNC(cmd_ping)
7123 char response[MAXLEN];
7124 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7125 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7126 irc_privmsg(cmd->parent->bot, channel->name, response);
7129 reply("CSMSG_PING_RESPONSE");
7133 static CHANSERV_FUNC(cmd_wut)
7137 char response[MAXLEN];
7138 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7139 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7140 irc_privmsg(cmd->parent->bot, channel->name, response);
7143 reply("CSMSG_WUT_RESPONSE");
7147 static CHANSERV_FUNC(cmd_8ball)
7149 unsigned int i, j, accum;
7154 for(i=1; i<argc; i++)
7155 for(j=0; argv[i][j]; j++)
7156 accum = (accum << 5) - accum + toupper(argv[i][j]);
7157 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7160 char response[MAXLEN];
7161 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7162 irc_privmsg(cmd->parent->bot, channel->name, response);
7165 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7169 static CHANSERV_FUNC(cmd_d)
7171 unsigned long sides, count, modifier, ii, total;
7172 char response[MAXLEN], *sep;
7176 if((count = strtoul(argv[1], &sep, 10)) < 1)
7186 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7187 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7191 else if((sep[0] == '-') && isdigit(sep[1]))
7192 modifier = strtoul(sep, NULL, 10);
7193 else if((sep[0] == '+') && isdigit(sep[1]))
7194 modifier = strtoul(sep+1, NULL, 10);
7201 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7206 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7209 for(total = ii = 0; ii < count; ++ii)
7210 total += (rand() % sides) + 1;
7213 if((count > 1) || modifier)
7215 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7216 sprintf(response, fmt, total, count, sides, modifier);
7220 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7221 sprintf(response, fmt, total, sides);
7224 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7226 send_message_type(4, user, cmd->parent->bot, "%s", response);
7230 static CHANSERV_FUNC(cmd_huggle)
7232 /* CTCP must be via PRIVMSG, never notice */
7234 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7236 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7241 chanserv_adjust_limit(void *data)
7243 struct mod_chanmode change;
7244 struct chanData *cData = data;
7245 struct chanNode *channel = cData->channel;
7248 if(IsSuspended(cData))
7251 cData->limitAdjusted = now;
7252 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7253 if(cData->modes.modes_set & MODE_LIMIT)
7255 if(limit > cData->modes.new_limit)
7256 limit = cData->modes.new_limit;
7257 else if(limit == cData->modes.new_limit)
7261 mod_chanmode_init(&change);
7262 change.modes_set = MODE_LIMIT;
7263 change.new_limit = limit;
7264 mod_chanmode_announce(chanserv, channel, &change);
7268 handle_new_channel(struct chanNode *channel)
7270 struct chanData *cData;
7272 if(!(cData = channel->channel_info))
7275 if(cData->modes.modes_set || cData->modes.modes_clear)
7276 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7278 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7279 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7282 void handle_new_channel_created(char *chan, struct userNode *user) {
7283 if(user->handle_info && chanserv_conf.new_channel_authed) {
7284 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7285 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7286 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7288 if(chanserv_conf.new_channel_msg)
7289 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7292 /* Welcome to my worst nightmare. Warning: Read (or modify)
7293 the code below at your own risk. */
7295 handle_join(struct modeNode *mNode)
7297 struct mod_chanmode change;
7298 struct userNode *user = mNode->user;
7299 struct chanNode *channel = mNode->channel;
7300 struct chanData *cData;
7301 struct userData *uData = NULL;
7302 struct banData *bData;
7303 struct handle_info *handle;
7304 unsigned int modes = 0, info = 0;
7308 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7311 cData = channel->channel_info;
7312 if(channel->members.used > cData->max) {
7313 cData->max = channel->members.used;
7314 cData->max_time = now;
7317 for(i = 0; i < channel->invited.used; i++)
7319 if(channel->invited.list[i] == user) {
7320 userList_remove(&channel->invited, user);
7324 /* Check for bans. If they're joining through a ban, one of two
7326 * 1: Join during a netburst, by riding the break. Kick them
7327 * unless they have ops or voice in the channel.
7328 * 2: They're allowed to join through the ban (an invite in
7329 * ircu2.10, or a +e on Hybrid, or something).
7330 * If they're not joining through a ban, and the banlist is not
7331 * full, see if they're on the banlist for the channel. If so,
7334 if(user->uplink->burst && !mNode->modes)
7337 for(ii = 0; ii < channel->banlist.used; ii++)
7339 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7341 /* Riding a netburst. Naughty. */
7342 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7348 mod_chanmode_init(&change);
7350 if(channel->banlist.used < MAXBANS)
7352 /* Not joining through a ban. */
7353 for(bData = cData->bans;
7354 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7355 bData = bData->next);
7359 char kick_reason[MAXLEN];
7360 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7362 bData->triggered = now;
7363 if(bData != cData->bans)
7365 /* Shuffle the ban to the head of the list. */
7367 bData->next->prev = bData->prev;
7369 bData->prev->next = bData->next;
7372 bData->next = cData->bans;
7375 cData->bans->prev = bData;
7376 cData->bans = bData;
7379 change.args[0].mode = MODE_BAN;
7380 change.args[0].u.hostmask = bData->mask;
7381 mod_chanmode_announce(chanserv, channel, &change);
7382 KickChannelUser(user, channel, chanserv, kick_reason);
7387 /* ChanServ will not modify the limits in join-flooded channels,
7388 or when there are enough slots left below the limit. */
7389 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7390 && !channel->join_flooded
7391 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7393 /* The user count has begun "bumping" into the channel limit,
7394 so set a timer to raise the limit a bit. Any previous
7395 timers are removed so three incoming users within the delay
7396 results in one limit change, not three. */
7398 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7399 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7402 if(channel->join_flooded)
7404 /* don't automatically give ops or voice during a join flood */
7406 else if(cData->lvlOpts[lvlGiveOps] == 0)
7407 modes |= MODE_CHANOP;
7408 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7409 modes |= MODE_VOICE;
7411 greeting = cData->greeting;
7412 if(user->handle_info)
7414 handle = user->handle_info;
7416 if(IsHelper(user) && !IsHelping(user))
7419 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7421 if(channel == chanserv_conf.support_channels.list[ii])
7423 HANDLE_SET_FLAG(user->handle_info, HELPING);
7429 uData = GetTrueChannelAccess(cData, handle);
7430 if(uData && !IsUserSuspended(uData))
7432 /* Ops and above were handled by the above case. */
7433 if(IsUserAutoOp(uData))
7435 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7436 modes |= MODE_CHANOP;
7437 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7438 modes |= MODE_VOICE;
7440 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7441 cData->visited = now;
7442 if(cData->user_greeting)
7443 greeting = cData->user_greeting;
7445 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7446 && ((now - uData->seen) >= chanserv_conf.info_delay)
7454 /* If user joining normally (not during burst), apply op or voice,
7455 * and send greeting/userinfo as appropriate.
7457 if(!user->uplink->burst)
7461 if(modes & MODE_CHANOP)
7462 modes &= ~MODE_VOICE;
7463 change.args[0].mode = modes;
7464 change.args[0].u.member = mNode;
7465 mod_chanmode_announce(chanserv, channel, &change);
7468 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7469 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7470 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7476 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7478 struct mod_chanmode change;
7479 struct userData *channel;
7480 unsigned int ii, jj;
7482 if(!user->handle_info)
7485 mod_chanmode_init(&change);
7487 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7489 struct chanNode *cn;
7490 struct modeNode *mn;
7491 if(IsUserSuspended(channel)
7492 || IsSuspended(channel->channel)
7493 || !(cn = channel->channel->channel))
7496 mn = GetUserMode(cn, user);
7499 if(!IsUserSuspended(channel)
7500 && IsUserAutoInvite(channel)
7501 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7503 && !user->uplink->burst)
7504 irc_invite(chanserv, user, cn);
7508 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7509 channel->channel->visited = now;
7511 if(IsUserAutoOp(channel))
7513 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7514 change.args[0].mode = MODE_CHANOP;
7515 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7516 change.args[0].mode = MODE_VOICE;
7518 change.args[0].mode = 0;
7519 change.args[0].u.member = mn;
7520 if(change.args[0].mode)
7521 mod_chanmode_announce(chanserv, cn, &change);
7524 channel->seen = now;
7525 channel->present = 1;
7528 for(ii = 0; ii < user->channels.used; ++ii)
7530 struct chanNode *chan = user->channels.list[ii]->channel;
7531 struct banData *ban;
7533 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7534 || !chan->channel_info
7535 || IsSuspended(chan->channel_info))
7537 for(jj = 0; jj < chan->banlist.used; ++jj)
7538 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7540 if(jj < chan->banlist.used)
7542 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7544 char kick_reason[MAXLEN];
7545 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7547 change.args[0].mode = MODE_BAN;
7548 change.args[0].u.hostmask = ban->mask;
7549 mod_chanmode_announce(chanserv, chan, &change);
7550 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7551 KickChannelUser(user, chan, chanserv, kick_reason);
7552 ban->triggered = now;
7557 if(IsSupportHelper(user))
7559 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7561 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7563 HANDLE_SET_FLAG(user->handle_info, HELPING);
7571 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7573 struct chanData *cData;
7574 struct userData *uData;
7576 cData = mn->channel->channel_info;
7577 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7580 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7582 /* Allow for a bit of padding so that the limit doesn't
7583 track the user count exactly, which could get annoying. */
7584 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7586 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7587 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7591 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7593 scan_user_presence(uData, mn->user);
7595 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7596 cData->visited = now;
7599 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7602 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7603 struct chanNode *channel;
7604 struct userNode *exclude;
7605 /* When looking at the channel that is being /part'ed, we
7606 * have to skip over the client that is leaving. For
7607 * other channels, we must not do that.
7609 channel = chanserv_conf.support_channels.list[ii];
7610 exclude = (channel == mn->channel) ? mn->user : NULL;
7611 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7614 if(ii == chanserv_conf.support_channels.used)
7615 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7620 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7622 struct userData *uData;
7624 if(!channel->channel_info || !kicker || IsService(kicker)
7625 || (kicker == victim) || IsSuspended(channel->channel_info)
7626 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7629 if(protect_user(victim, kicker, channel->channel_info))
7631 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7632 KickChannelUser(kicker, channel, chanserv, reason);
7635 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7640 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7642 struct chanData *cData;
7644 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7647 cData = channel->channel_info;
7648 if(bad_topic(channel, user, channel->topic))
7650 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7651 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7652 SetChannelTopic(channel, chanserv, old_topic, 1);
7653 else if(cData->topic)
7654 SetChannelTopic(channel, chanserv, cData->topic, 1);
7657 /* With topicsnarf, grab the topic and save it as the default topic. */
7658 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7661 cData->topic = strdup(channel->topic);
7667 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7669 struct mod_chanmode *bounce = NULL;
7670 unsigned int bnc, ii;
7673 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7676 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7677 && mode_lock_violated(&channel->channel_info->modes, change))
7679 char correct[MAXLEN];
7680 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7681 mod_chanmode_format(&channel->channel_info->modes, correct);
7682 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7684 for(ii = bnc = 0; ii < change->argc; ++ii)
7686 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7688 const struct userNode *victim = change->args[ii].u.member->user;
7689 if(!protect_user(victim, user, channel->channel_info))
7692 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7695 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7696 bounce->args[bnc].u.member = GetUserMode(channel, user);
7697 if(bounce->args[bnc].u.member)
7701 bounce->args[bnc].mode = MODE_CHANOP;
7702 bounce->args[bnc].u.member = change->args[ii].u.member;
7704 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7706 else if(change->args[ii].mode & MODE_CHANOP)
7708 const struct userNode *victim = change->args[ii].u.member->user;
7709 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7712 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7713 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7714 bounce->args[bnc].u.member = change->args[ii].u.member;
7717 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7719 const char *ban = change->args[ii].u.hostmask;
7720 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7723 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7724 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7725 bounce->args[bnc].u.hostmask = strdup(ban);
7727 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7732 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7733 mod_chanmode_announce(chanserv, channel, bounce);
7734 for(ii = 0; ii < change->argc; ++ii)
7735 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7736 free((char*)bounce->args[ii].u.hostmask);
7737 mod_chanmode_free(bounce);
7742 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7744 struct chanNode *channel;
7745 struct banData *bData;
7746 struct mod_chanmode change;
7747 unsigned int ii, jj;
7748 char kick_reason[MAXLEN];
7750 mod_chanmode_init(&change);
7752 change.args[0].mode = MODE_BAN;
7753 for(ii = 0; ii < user->channels.used; ++ii)
7755 channel = user->channels.list[ii]->channel;
7756 /* Need not check for bans if they're opped or voiced. */
7757 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7759 /* Need not check for bans unless channel registration is active. */
7760 if(!channel->channel_info || IsSuspended(channel->channel_info))
7762 /* Look for a matching ban already on the channel. */
7763 for(jj = 0; jj < channel->banlist.used; ++jj)
7764 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7766 /* Need not act if we found one. */
7767 if(jj < channel->banlist.used)
7769 /* Look for a matching ban in this channel. */
7770 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7772 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7774 change.args[0].u.hostmask = bData->mask;
7775 mod_chanmode_announce(chanserv, channel, &change);
7776 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7777 KickChannelUser(user, channel, chanserv, kick_reason);
7778 bData->triggered = now;
7779 break; /* we don't need to check any more bans in the channel */
7784 static void handle_rename(struct handle_info *handle, const char *old_handle)
7786 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7790 dict_remove2(handle_dnrs, old_handle, 1);
7791 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7792 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7797 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7799 struct userNode *h_user;
7801 if(handle->channels)
7803 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7804 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7806 while(handle->channels)
7807 del_channel_user(handle->channels, 1);
7812 handle_server_link(UNUSED_ARG(struct server *server))
7814 struct chanData *cData;
7816 for(cData = channelList; cData; cData = cData->next)
7818 if(!IsSuspended(cData))
7819 cData->may_opchan = 1;
7820 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7821 && !cData->channel->join_flooded
7822 && ((cData->channel->limit - cData->channel->members.used)
7823 < chanserv_conf.adjust_threshold))
7825 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7826 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7832 chanserv_conf_read(void)
7836 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7837 struct mod_chanmode *change;
7838 struct string_list *strlist;
7839 struct chanNode *chan;
7842 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7844 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7847 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7848 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7849 chanserv_conf.support_channels.used = 0;
7850 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7852 for(ii = 0; ii < strlist->used; ++ii)
7854 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7857 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7859 channelList_append(&chanserv_conf.support_channels, chan);
7862 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7865 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7868 chan = AddChannel(str, now, str2, NULL);
7870 channelList_append(&chanserv_conf.support_channels, chan);
7872 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7873 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7874 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7875 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7876 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7877 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7878 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7879 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7880 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7881 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7882 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7883 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7884 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7885 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7886 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7887 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7888 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7889 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7890 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7891 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7892 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7893 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7894 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7895 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7896 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7897 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7898 str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7899 chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7900 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7901 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7902 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7904 NickChange(chanserv, str, 0);
7905 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7906 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7907 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7908 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7909 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7910 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7911 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7912 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7913 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7914 chanserv_conf.max_owned = str ? atoi(str) : 5;
7915 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7916 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7917 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7918 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7919 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7920 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7921 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7922 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7923 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7924 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7925 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7926 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7927 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7930 safestrncpy(mode_line, str, sizeof(mode_line));
7931 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7932 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7933 && (change->argc < 2))
7935 chanserv_conf.default_modes = *change;
7936 mod_chanmode_free(change);
7938 free_string_list(chanserv_conf.set_shows);
7939 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7941 strlist = string_list_copy(strlist);
7944 static const char *list[] = {
7945 /* free form text */
7946 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7947 /* options based on user level */
7948 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7949 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7950 /* multiple choice options */
7951 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7952 /* binary options */
7953 "DynLimit", "NoDelete", "expire", "Vote",
7957 strlist = alloc_string_list(ArrayLength(list)-1);
7958 for(ii=0; list[ii]; ii++)
7959 string_list_append(strlist, strdup(list[ii]));
7961 chanserv_conf.set_shows = strlist;
7962 /* We don't look things up now, in case the list refers to options
7963 * defined by modules initialized after this point. Just mark the
7964 * function list as invalid, so it will be initialized.
7966 set_shows_list.used = 0;
7967 free_string_list(chanserv_conf.eightball);
7968 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7971 strlist = string_list_copy(strlist);
7975 strlist = alloc_string_list(4);
7976 string_list_append(strlist, strdup("Yes."));
7977 string_list_append(strlist, strdup("No."));
7978 string_list_append(strlist, strdup("Maybe so."));
7980 chanserv_conf.eightball = strlist;
7981 free_string_list(chanserv_conf.old_ban_names);
7982 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7984 strlist = string_list_copy(strlist);
7986 strlist = alloc_string_list(2);
7987 chanserv_conf.old_ban_names = strlist;
7988 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7989 off_channel = str ? atoi(str) : 0;
7991 str = database_get_data(conf_node, "oper_chan", RECDB_QSTRING);
7994 chanserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
7998 chanserv_conf.oper_channel = NULL;
8003 chanserv_note_type_read(const char *key, struct record_data *rd)
8006 struct note_type *ntype;
8009 if(!(obj = GET_RECORD_OBJECT(rd)))
8011 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8014 if(!(ntype = chanserv_create_note_type(key)))
8016 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8020 /* Figure out set access */
8021 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8023 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8024 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8026 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8028 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8029 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8031 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8033 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8037 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8038 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8039 ntype->set_access.min_opserv = 0;
8042 /* Figure out visibility */
8043 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8044 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8045 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8046 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8047 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8048 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8049 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8050 ntype->visible_type = NOTE_VIS_ALL;
8052 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8054 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8055 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8059 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8061 struct vote_option *vOpt;
8064 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8066 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8070 vOpt = calloc(1, sizeof(*vOpt));
8071 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8072 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8073 vOpt->voted = str ? atoi(str) : 0;
8074 vOpt->option_id = str ? atoi(key) : 0;
8075 vOpt->option_str = strdup(key);
8076 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8080 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8082 struct handle_info *handle;
8083 struct userData *uData;
8084 char *seen, *inf, *flags, *voted, *votefor, *expires;
8085 unsigned long last_seen;
8086 unsigned short access_level;
8088 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8090 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8094 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8095 if(access_level > UL_OWNER)
8097 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8101 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8102 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8103 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8104 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8105 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8106 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8107 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8108 handle = get_handle_info(key);
8111 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8115 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8116 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8117 uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8119 if((uData->flags & USER_SUSPENDED) && uData->expires)
8121 if(uData->expires > now)
8122 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8124 uData->flags &= ~USER_SUSPENDED;
8127 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8128 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8136 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8138 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8139 unsigned long set_time, triggered_time, expires_time;
8141 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8143 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8147 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8148 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8149 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8150 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8151 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8152 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8153 if (!reason || !owner)
8156 set_time = set ? strtoul(set, NULL, 0) : now;
8157 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8159 expires_time = strtoul(s_expires, NULL, 0);
8161 expires_time = set_time + atoi(s_duration);
8165 if(!reason || (expires_time && (expires_time < now)))
8168 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8171 static struct suspended *
8172 chanserv_read_suspended(dict_t obj)
8174 struct suspended *suspended = calloc(1, sizeof(*suspended));
8178 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8179 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8180 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8181 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8182 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8183 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8184 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8185 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8186 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8187 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8191 static struct giveownership *
8192 chanserv_read_giveownership(dict_t obj)
8194 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8198 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8199 giveownership->staff_issuer = str ? strdup(str) : NULL;
8201 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8203 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8204 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8206 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8207 giveownership->reason = str ? strdup(str) : NULL;
8208 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8209 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8211 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8212 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8213 return giveownership;
8217 chanserv_channel_read(const char *key, struct record_data *hir)
8219 struct suspended *suspended;
8220 struct giveownership *giveownership;
8221 struct mod_chanmode *modes;
8222 struct chanNode *cNode;
8223 struct chanData *cData;
8224 struct dict *channel, *obj;
8225 char *str, *argv[10];
8229 channel = hir->d.object;
8231 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8234 cNode = AddChannel(key, now, NULL, NULL);
8237 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8240 cData = register_channel(cNode, str);
8243 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8247 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8249 enum levelOption lvlOpt;
8250 enum charOption chOpt;
8252 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8253 cData->flags = atoi(str);
8255 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8257 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8259 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8260 else if(levelOptions[lvlOpt].old_flag)
8262 if(cData->flags & levelOptions[lvlOpt].old_flag)
8263 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8265 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8269 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8271 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8273 cData->chOpts[chOpt] = str[0];
8276 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8278 enum levelOption lvlOpt;
8279 enum charOption chOpt;
8282 cData->flags = base64toint(str, 5);
8283 count = strlen(str += 5);
8284 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8287 if(levelOptions[lvlOpt].old_flag)
8289 if(cData->flags & levelOptions[lvlOpt].old_flag)
8290 lvl = levelOptions[lvlOpt].flag_value;
8292 lvl = levelOptions[lvlOpt].default_value;
8294 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8296 case 'c': lvl = UL_COOWNER; break;
8297 case 'm': lvl = UL_MASTER; break;
8298 case 'n': lvl = UL_OWNER+1; break;
8299 case 'o': lvl = UL_OP; break;
8300 case 'p': lvl = UL_PEON; break;
8301 case 'w': lvl = UL_OWNER; break;
8302 default: lvl = 0; break;
8304 cData->lvlOpts[lvlOpt] = lvl;
8306 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8307 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8310 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8312 cData->expiry = atoi(str);
8313 if(cData->expiry > 0) {
8314 if(cData->expiry > now) {
8315 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8317 timeq_add(1, chanserv_expire_channel, cData);
8324 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8326 suspended = chanserv_read_suspended(obj);
8327 cData->suspended = suspended;
8328 suspended->cData = cData;
8329 /* We could use suspended->expires and suspended->revoked to
8330 * set the CHANNEL_SUSPENDED flag, but we don't. */
8332 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8334 suspended = calloc(1, sizeof(*suspended));
8335 suspended->issued = 0;
8336 suspended->revoked = 0;
8337 suspended->suspender = strdup(str);
8338 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8339 suspended->expires = str ? atoi(str) : 0;
8340 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8341 suspended->reason = strdup(str ? str : "No reason");
8342 suspended->previous = NULL;
8343 cData->suspended = suspended;
8344 suspended->cData = cData;
8348 cData->flags &= ~CHANNEL_SUSPENDED;
8349 suspended = NULL; /* to squelch a warning */
8352 if(IsSuspended(cData)) {
8353 if(suspended->expires > now)
8354 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8355 else if(suspended->expires)
8356 cData->flags &= ~CHANNEL_SUSPENDED;
8359 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8361 giveownership = chanserv_read_giveownership(obj);
8362 cData->giveownership = giveownership;
8365 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8366 struct mod_chanmode change;
8367 mod_chanmode_init(&change);
8369 change.args[0].mode = MODE_CHANOP;
8370 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8371 mod_chanmode_announce(chanserv, cNode, &change);
8374 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8375 cData->registered = str ? strtoul(str, NULL, 0) : now;
8376 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8377 cData->visited = str ? strtoul(str, NULL, 0) : now;
8378 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8379 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8380 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8381 cData->max = str ? atoi(str) : 0;
8382 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8383 cData->max_time = str ? atoi(str) : 0;
8384 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8385 cData->greeting = str ? strdup(str) : NULL;
8386 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8387 cData->user_greeting = str ? strdup(str) : NULL;
8388 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8389 cData->topic_mask = str ? strdup(str) : NULL;
8390 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8391 cData->topic = str ? strdup(str) : NULL;
8393 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8395 cData->vote = str ? strdup(str) : NULL;
8396 dict_delete(cData->vote_options);
8397 cData->vote_options = dict_new();
8398 dict_set_free_data(cData->vote_options, free_vote_options);
8399 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8400 cData->vote_start = str ? atoi(str) : 0;
8401 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8402 for(it = dict_first(obj); it; it = iter_next(it)) {
8403 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8407 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8408 for(it = dict_first(obj); it; it = iter_next(it))
8410 struct record_data *rd = iter_data(it);
8411 if(rd->type != RECDB_QSTRING) continue;
8412 int advtopic_index = atoi(iter_key(it));
8413 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8414 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8417 if(!IsSuspended(cData)
8418 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8419 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8420 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8421 cData->modes = *modes;
8423 cData->modes.modes_set |= MODE_REGISTERED;
8424 if(cData->modes.argc > 1)
8425 cData->modes.argc = 1;
8426 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8427 mod_chanmode_free(modes);
8430 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8431 for(it = dict_first(obj); it; it = iter_next(it))
8432 user_read_helper(iter_key(it), iter_data(it), cData);
8434 if(!cData->users && !IsProtected(cData))
8436 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8437 unregister_channel(cData, "has empty user list.");
8441 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8442 for(it = dict_first(obj); it; it = iter_next(it))
8443 ban_read_helper(iter_key(it), iter_data(it), cData);
8445 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8446 for(it = dict_first(obj); it; it = iter_next(it))
8448 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8449 struct record_data *rd = iter_data(it);
8450 const char *note, *setter;
8452 if(rd->type != RECDB_OBJECT)
8454 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8458 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8460 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8462 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8466 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8467 if(!setter) setter = "<unknown>";
8468 chanserv_add_channel_note(cData, ntype, setter, note);
8476 chanserv_dnr_read(const char *key, struct record_data *hir)
8478 const char *setter, *reason, *str;
8479 struct do_not_register *dnr;
8480 unsigned long expiry;
8482 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8485 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8488 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8491 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8494 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8495 expiry = str ? strtoul(str, NULL, 0) : 0;
8496 if(expiry && expiry <= now)
8498 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8501 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8503 dnr->set = atoi(str);
8509 chanserv_saxdb_read(struct dict *database)
8511 struct dict *section;
8514 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8515 for(it = dict_first(section); it; it = iter_next(it))
8516 chanserv_note_type_read(iter_key(it), iter_data(it));
8518 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8519 for(it = dict_first(section); it; it = iter_next(it))
8520 chanserv_channel_read(iter_key(it), iter_data(it));
8522 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8523 for(it = dict_first(section); it; it = iter_next(it))
8524 chanserv_dnr_read(iter_key(it), iter_data(it));
8530 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8532 int high_present = 0;
8533 saxdb_start_record(ctx, KEY_USERS, 1);
8534 for(; uData; uData = uData->next)
8536 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8538 saxdb_start_record(ctx, uData->handle->handle, 0);
8539 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8540 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8542 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8544 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8545 if(uData->channel->vote && uData->voted)
8546 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8547 if(uData->channel->vote && uData->votefor)
8548 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8550 saxdb_write_string(ctx, KEY_INFO, uData->info);
8551 saxdb_end_record(ctx);
8553 saxdb_end_record(ctx);
8554 return high_present;
8558 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8562 saxdb_start_record(ctx, KEY_BANS, 1);
8563 for(; bData; bData = bData->next)
8565 saxdb_start_record(ctx, bData->mask, 0);
8566 saxdb_write_int(ctx, KEY_SET, bData->set);
8567 if(bData->triggered)
8568 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8570 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8572 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8574 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8575 saxdb_end_record(ctx);
8577 saxdb_end_record(ctx);
8581 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8583 saxdb_start_record(ctx, name, 0);
8584 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8585 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8587 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8589 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8591 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8593 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8594 saxdb_end_record(ctx);
8598 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8600 saxdb_start_record(ctx, name, 0);
8601 if(giveownership->staff_issuer)
8602 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8603 if(giveownership->old_owner)
8604 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8605 if(giveownership->target)
8606 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8607 if(giveownership->target_access)
8608 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8609 if(giveownership->reason)
8610 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8611 if(giveownership->issued)
8612 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8613 if(giveownership->previous)
8614 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8615 saxdb_end_record(ctx);
8619 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8623 enum levelOption lvlOpt;
8624 enum charOption chOpt;
8627 saxdb_start_record(ctx, channel->channel->name, 1);
8629 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8630 saxdb_write_int(ctx, KEY_MAX, channel->max);
8631 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8633 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8634 if(channel->registrar)
8635 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8636 if(channel->greeting)
8637 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8638 if(channel->user_greeting)
8639 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8640 if(channel->topic_mask)
8641 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8642 if(channel->suspended)
8643 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8644 if(channel->giveownership)
8645 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8647 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8650 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8651 if(channel->vote_start)
8652 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8653 if (dict_size(channel->vote_options)) {
8654 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8655 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8656 struct vote_option *vOpt = iter_data(it);
8658 sprintf(str,"%i",vOpt->option_id);
8659 saxdb_start_record(ctx, str, 0);
8661 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8663 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8664 saxdb_end_record(ctx);
8666 saxdb_end_record(ctx);
8670 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8671 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8672 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8673 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8674 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8676 buf[0] = channel->chOpts[chOpt];
8678 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8680 saxdb_end_record(ctx);
8682 if(channel->modes.modes_set || channel->modes.modes_clear)
8684 mod_chanmode_format(&channel->modes, buf);
8685 saxdb_write_string(ctx, KEY_MODES, buf);
8688 high_present = chanserv_write_users(ctx, channel->users);
8689 chanserv_write_bans(ctx, channel->bans);
8691 if(channel->flags & CHANNEL_ADVTOPIC) {
8692 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8694 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8695 if(channel->advtopic[advtopic_index])
8696 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8698 saxdb_end_record(ctx);
8701 if(dict_size(channel->notes))
8705 saxdb_start_record(ctx, KEY_NOTES, 1);
8706 for(it = dict_first(channel->notes); it; it = iter_next(it))
8708 struct note *note = iter_data(it);
8709 saxdb_start_record(ctx, iter_key(it), 0);
8710 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8711 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8712 saxdb_end_record(ctx);
8714 saxdb_end_record(ctx);
8717 if(channel->ownerTransfer)
8718 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8719 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8720 saxdb_end_record(ctx);
8724 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8728 saxdb_start_record(ctx, ntype->name, 0);
8729 switch(ntype->set_access_type)
8731 case NOTE_SET_CHANNEL_ACCESS:
8732 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8734 case NOTE_SET_CHANNEL_SETTER:
8735 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8737 case NOTE_SET_PRIVILEGED: default:
8738 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8741 switch(ntype->visible_type)
8743 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8744 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8745 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8747 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8748 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8749 saxdb_end_record(ctx);
8753 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8755 struct do_not_register *dnr;
8756 dict_iterator_t it, next;
8758 for(it = dict_first(dnrs); it; it = next)
8760 next = iter_next(it);
8761 dnr = iter_data(it);
8762 if(dnr->expires && dnr->expires <= now)
8764 dict_remove(dnrs, iter_key(it));
8767 saxdb_start_record(ctx, dnr->chan_name, 0);
8769 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8771 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8772 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8773 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8774 saxdb_end_record(ctx);
8779 chanserv_saxdb_write(struct saxdb_context *ctx)
8782 struct chanData *channel;
8785 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8786 for(it = dict_first(note_types); it; it = iter_next(it))
8787 chanserv_write_note_type(ctx, iter_data(it));
8788 saxdb_end_record(ctx);
8791 saxdb_start_record(ctx, KEY_DNR, 1);
8792 write_dnrs_helper(ctx, handle_dnrs);
8793 write_dnrs_helper(ctx, plain_dnrs);
8794 write_dnrs_helper(ctx, mask_dnrs);
8795 saxdb_end_record(ctx);
8798 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8799 for(channel = channelList; channel; channel = channel->next)
8800 chanserv_write_channel(ctx, channel);
8801 saxdb_end_record(ctx);
8807 chanserv_db_cleanup(void) {
8809 unreg_part_func(handle_part);
8811 unregister_channel(channelList, "terminating.");
8812 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8813 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8814 free(chanserv_conf.support_channels.list);
8815 dict_delete(handle_dnrs);
8816 dict_delete(plain_dnrs);
8817 dict_delete(mask_dnrs);
8818 dict_delete(note_types);
8819 free_string_list(chanserv_conf.eightball);
8820 free_string_list(chanserv_conf.old_ban_names);
8821 free_string_list(chanserv_conf.set_shows);
8822 free(set_shows_list.list);
8823 free(uset_shows_list.list);
8826 struct userData *helper = helperList;
8827 helperList = helperList->next;
8832 #if defined(GCC_VARMACROS)
8833 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8834 #elif defined(C99_VARMACROS)
8835 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8837 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8838 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8841 init_chanserv(const char *nick)
8843 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8844 conf_register_reload(chanserv_conf_read);
8848 reg_server_link_func(handle_server_link);
8849 reg_new_channel_func(handle_new_channel);
8850 reg_join_func(handle_join);
8851 reg_part_func(handle_part);
8852 reg_kick_func(handle_kick);
8853 reg_topic_func(handle_topic);
8854 reg_mode_change_func(handle_mode);
8855 reg_nick_change_func(handle_nick_change);
8856 reg_auth_func(handle_auth);
8859 reg_handle_rename_func(handle_rename);
8860 reg_unreg_func(handle_unreg);
8862 handle_dnrs = dict_new();
8863 dict_set_free_data(handle_dnrs, free);
8864 plain_dnrs = dict_new();
8865 dict_set_free_data(plain_dnrs, free);
8866 mask_dnrs = dict_new();
8867 dict_set_free_data(mask_dnrs, free);
8869 reg_svccmd_unbind_func(handle_svccmd_unbind);
8870 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8871 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8872 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8873 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8874 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8875 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8876 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8877 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8878 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8879 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8880 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8881 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8882 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8884 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8885 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8887 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8888 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8889 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8890 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8891 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8893 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8894 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8895 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8896 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8897 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8899 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8900 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8901 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8902 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8904 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8905 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8906 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8907 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8908 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8909 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8910 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8911 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8913 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8914 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8915 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8916 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8917 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8918 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8919 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8920 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8921 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8922 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8923 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8924 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8925 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8926 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8927 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8929 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8930 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8931 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8932 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8933 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8935 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8936 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8938 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8939 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8940 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8941 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8942 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8943 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8944 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8945 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8946 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8947 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8948 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8950 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8951 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8953 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8954 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8955 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8956 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8958 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8959 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8960 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8961 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8962 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8964 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8965 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8966 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8967 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8968 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8969 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8971 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8972 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8973 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8974 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8975 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8976 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8977 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8978 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8980 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8982 /* Channel options */
8983 DEFINE_CHANNEL_OPTION(defaulttopic);
8984 DEFINE_CHANNEL_OPTION(topicmask);
8985 DEFINE_CHANNEL_OPTION(greeting);
8986 DEFINE_CHANNEL_OPTION(usergreeting);
8987 DEFINE_CHANNEL_OPTION(modes);
8988 DEFINE_CHANNEL_OPTION(enfops);
8989 DEFINE_CHANNEL_OPTION(giveops);
8990 DEFINE_CHANNEL_OPTION(protect);
8991 DEFINE_CHANNEL_OPTION(enfmodes);
8992 DEFINE_CHANNEL_OPTION(enftopic);
8993 DEFINE_CHANNEL_OPTION(pubcmd);
8994 DEFINE_CHANNEL_OPTION(givevoice);
8995 DEFINE_CHANNEL_OPTION(userinfo);
8996 DEFINE_CHANNEL_OPTION(dynlimit);
8997 DEFINE_CHANNEL_OPTION(topicsnarf);
8998 DEFINE_CHANNEL_OPTION(vote);
8999 DEFINE_CHANNEL_OPTION(nodelete);
9000 DEFINE_CHANNEL_OPTION(toys);
9001 DEFINE_CHANNEL_OPTION(setters);
9002 DEFINE_CHANNEL_OPTION(topicrefresh);
9003 DEFINE_CHANNEL_OPTION(ctcpusers);
9004 DEFINE_CHANNEL_OPTION(ctcpreaction);
9005 DEFINE_CHANNEL_OPTION(inviteme);
9006 DEFINE_CHANNEL_OPTION(advtopic);
9007 DEFINE_CHANNEL_OPTION(unreviewed);
9008 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
9009 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9010 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9012 DEFINE_CHANNEL_OPTION(offchannel);
9013 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9015 /* Alias set topic to set defaulttopic for compatibility. */
9016 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9019 DEFINE_USER_OPTION(noautoop);
9020 DEFINE_USER_OPTION(autoinvite);
9021 DEFINE_USER_OPTION(info);
9023 /* Alias uset autovoice to uset autoop. */
9024 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9026 note_types = dict_new();
9027 dict_set_free_data(note_types, chanserv_deref_note_type);
9030 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9031 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9032 service_register(chanserv)->trigger = '!';
9033 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9035 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9037 if(chanserv_conf.channel_expire_frequency)
9038 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9040 if(chanserv_conf.dnr_expire_frequency)
9041 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9043 if(chanserv_conf.refresh_period)
9045 unsigned long next_refresh;
9046 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9047 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9050 reg_exit_func(chanserv_db_cleanup);
9051 message_register_table(msgtab);