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_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
61 #define KEY_REVOKE_MODE_A "revoke_mode_a"
62 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
63 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
64 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
66 /* ChanServ database */
67 #define KEY_CHANNELS "channels"
68 #define KEY_NOTE_TYPES "note_types"
70 /* Note type parameters */
71 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
72 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
73 #define KEY_NOTE_SETTER_ACCESS "setter_access"
74 #define KEY_NOTE_VISIBILITY "visibility"
75 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
76 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
77 #define KEY_NOTE_VIS_ALL "all"
78 #define KEY_NOTE_MAX_LENGTH "max_length"
79 #define KEY_NOTE_SETTER "setter"
80 #define KEY_NOTE_NOTE "note"
82 /* Do-not-register channels */
84 #define KEY_DNR_SET "set"
85 #define KEY_DNR_SETTER "setter"
86 #define KEY_DNR_REASON "reason"
89 #define KEY_REGISTERED "registered"
90 #define KEY_REGISTRAR "registrar"
91 #define KEY_SUSPENDED "suspended"
92 #define KEY_PREVIOUS "previous"
93 #define KEY_SUSPENDER "suspender"
94 #define KEY_ISSUED "issued"
95 #define KEY_REVOKED "revoked"
96 #define KEY_SUSPEND_EXPIRES "suspend_expires"
97 #define KEY_SUSPEND_REASON "suspend_reason"
98 #define KEY_GIVEOWNERSHIP "giveownership"
99 #define KEY_STAFF_ISSUER "staff_issuer"
100 #define KEY_OLD_OWNER "old_owner"
101 #define KEY_TARGET "target"
102 #define KEY_TARGET_ACCESS "target_access"
103 #define KEY_VISITED "visited"
104 #define KEY_TOPIC "topic"
105 #define KEY_GREETING "greeting"
106 #define KEY_USER_GREETING "user_greeting"
107 #define KEY_MODES "modes"
108 #define KEY_FLAGS "flags"
109 #define KEY_OPTIONS "options"
110 #define KEY_USERS "users"
111 #define KEY_BANS "bans"
112 #define KEY_MAX "max"
113 #define KEY_MAX_TIME "max_time"
114 #define KEY_NOTES "notes"
115 #define KEY_TOPIC_MASK "topic_mask"
116 #define KEY_ADVTOPIC_ENTRIES "adv_topic"
117 #define KEY_OWNER_TRANSFER "owner_transfer"
118 #define KEY_EXPIRE "expire"
121 #define KEY_LEVEL "level"
122 #define KEY_INFO "info"
123 #define KEY_SEEN "seen"
126 #define KEY_VOTE "vote"
127 #define KEY_VOTE_START "votestart"
128 #define KEY_VOTE_OPTIONS "voptions"
129 #define KEY_VOTE_OPTION_NAME "voptionname"
130 #define KEY_VOTE_VOTED "vvoted"
131 #define KEY_VOTE_VOTEDFOR "vvotefor"
132 #define KEY_VOTE_OPTION_ID "voptionid"
133 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
136 #define KEY_OWNER "owner"
137 #define KEY_REASON "reason"
138 #define KEY_SET "set"
139 #define KEY_DURATION "duration"
140 #define KEY_EXPIRES "expires"
141 #define KEY_TRIGGERED "triggered"
143 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
144 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
145 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
147 /* Administrative messages */
148 static const struct message_entry msgtab[] = {
149 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
151 /* Channel registration */
152 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
153 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
154 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
155 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
156 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
157 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
159 /* Do-not-register channels */
160 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
161 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
162 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
163 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
164 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
165 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
166 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
167 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
168 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
169 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
170 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
171 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
172 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
173 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
175 /* Channel unregistration */
176 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
177 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
178 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
179 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
182 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
183 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
185 /* Channel merging */
186 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
187 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
188 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
189 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
190 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
192 /* Handle unregistration */
193 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
196 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
197 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
198 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
199 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
200 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
201 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
202 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
203 { "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." },
204 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
205 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
206 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
207 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
208 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
209 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
211 /* Removing yourself from a channel. */
212 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
213 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
214 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
216 /* User management */
217 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
218 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
219 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
220 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
221 { "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." },
222 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
223 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
224 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
226 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
227 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
228 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
229 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
230 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
231 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
232 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
235 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
236 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
237 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
238 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
239 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
240 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
241 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
242 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
243 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
244 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
245 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
246 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
247 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
248 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
249 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
250 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
252 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
254 /* Channel management */
255 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
256 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
257 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
259 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
260 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
261 { "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" },
262 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
263 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
264 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
265 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
266 { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
268 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
269 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
270 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
271 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
272 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
273 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
274 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
275 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
276 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
277 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
278 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
279 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
280 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
281 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
282 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
283 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
284 { "CSMSG_SET_ADVTOPIC", "$bAdvTopic $b %s" },
285 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
286 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
287 { "CSMSG_SET_MODES", "$bModes $b %s" },
288 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
289 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
290 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
291 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
292 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
293 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
294 { "CSMSG_SET_VOTE", "$bVote $b %d" },
295 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
296 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
297 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
298 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
299 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
300 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
301 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
302 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
303 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
304 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
305 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
306 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
307 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
308 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
309 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
310 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
311 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
312 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
313 { "CSMSG_USET_INFO", "$bInfo $b %s" },
315 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
316 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
317 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
318 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
319 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
320 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
321 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
322 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
323 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
324 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
325 { "CSMSG_PROTECT_NONE", "No users will be protected." },
326 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
327 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
328 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
329 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
330 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
331 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
332 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
333 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
334 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
335 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
336 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
337 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
339 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
340 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
341 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
342 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
343 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
344 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
345 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
346 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
348 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
349 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
350 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
352 /* Channel userlist */
353 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
354 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
355 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
356 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
357 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
359 /* Channel note list */
360 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
361 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
362 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
363 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
364 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
365 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
366 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
367 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
368 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
369 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
370 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
371 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
372 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
373 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
374 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
376 /* Channel [un]suspension */
377 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
378 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
379 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
380 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
381 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
382 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
383 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
385 /* Access information */
386 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
387 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
388 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
389 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
390 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
391 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
392 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
393 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
394 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
395 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
396 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
397 { "CSMSG_UC_H_TITLE", "network helper" },
398 { "CSMSG_LC_H_TITLE", "support helper" },
399 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
400 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
401 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
404 /* Seen information */
405 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
406 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
407 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
408 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
410 /* Names information */
411 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
412 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
414 /* Channel information */
415 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
416 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
417 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
418 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
419 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
420 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
421 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
422 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
423 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
424 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
425 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
426 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
427 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
428 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
435 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
436 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
438 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
439 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
440 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
441 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
443 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
444 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
445 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
446 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
447 { "CSMSG_PEEK_OPS", "$bOps:$b" },
448 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
450 /* Network information */
451 { "CSMSG_NETWORK_INFO", "Network Information:" },
452 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
453 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
454 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
455 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
456 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
457 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
458 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
459 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
462 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
463 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
464 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
466 /* Channel searches */
467 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
468 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
469 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
470 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
472 /* Channel configuration */
473 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
474 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
475 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
476 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
479 { "CSMSG_USER_OPTIONS", "User Options:" },
480 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
483 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
484 { "CSMSG_PING_RESPONSE", "Pong!" },
485 { "CSMSG_WUT_RESPONSE", "wut" },
486 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
487 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
488 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
489 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
490 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
491 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
492 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
495 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
496 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
497 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
498 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
499 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
500 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
501 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
502 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
503 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
504 { "CSMSG_VOTE_QUESTION", "Question: %s" },
505 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
506 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
507 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
508 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
509 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
510 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
511 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
512 { "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." },
513 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
514 { "CSMSG_VOTE_VOTED", "You have already voted." },
515 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
516 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
517 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
518 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
521 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
525 /* eject_user and unban_user flags */
526 #define ACTION_KICK 0x0001
527 #define ACTION_BAN 0x0002
528 #define ACTION_ADD_BAN 0x0004
529 #define ACTION_ADD_TIMED_BAN 0x0008
530 #define ACTION_UNBAN 0x0010
531 #define ACTION_DEL_BAN 0x0020
533 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
534 #define MODELEN 40 + KEYLEN
538 #define CSFUNC_ARGS user, channel, argc, argv, cmd
540 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
541 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
542 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
543 reply("MSG_MISSING_PARAMS", argv[0]); \
547 DECLARE_LIST(dnrList, struct do_not_register *);
548 DEFINE_LIST(dnrList, struct do_not_register *)
550 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
552 struct userNode *chanserv;
555 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
556 static struct log_type *CS_LOG;
560 struct channelList support_channels;
561 struct mod_chanmode default_modes;
563 unsigned long db_backup_frequency;
564 unsigned long channel_expire_frequency;
565 unsigned long dnr_expire_frequency;
567 unsigned long invited_timeout;
569 unsigned long info_delay;
570 unsigned long adjust_delay;
571 unsigned long channel_expire_delay;
572 unsigned int nodelete_level;
574 unsigned int adjust_threshold;
575 int join_flood_threshold;
577 unsigned int greeting_length;
578 unsigned int refresh_period;
579 unsigned int giveownership_period;
581 unsigned int max_owned;
582 unsigned int max_chan_users;
583 unsigned int max_chan_bans;
584 unsigned int max_userinfo_length;
586 unsigned int revoke_mode_a;
588 struct string_list *set_shows;
589 struct string_list *eightball;
590 struct string_list *old_ban_names;
592 const char *ctcp_short_ban_duration;
593 const char *ctcp_long_ban_duration;
595 const char *irc_operator_epithet;
596 const char *network_helper_epithet;
597 const char *support_helper_epithet;
599 const char *new_channel_authed;
600 const char *new_channel_unauthed;
601 const char *new_channel_msg;
606 struct userNode *user;
607 struct userNode *bot;
608 struct chanNode *channel;
610 unsigned short lowest;
611 unsigned short highest;
612 struct userData **users;
613 struct helpfile_table table;
618 struct userNode *user;
619 struct chanNode *chan;
622 enum note_access_type
624 NOTE_SET_CHANNEL_ACCESS,
625 NOTE_SET_CHANNEL_SETTER,
629 enum note_visible_type
632 NOTE_VIS_CHANNEL_USERS,
638 enum note_access_type set_access_type;
640 unsigned int min_opserv;
641 unsigned short min_ulevel;
643 enum note_visible_type visible_type;
644 unsigned int max_length;
651 struct note_type *type;
652 char setter[NICKSERV_HANDLE_LEN+1];
656 static unsigned int registered_channels;
657 static unsigned int banCount;
659 static const struct {
662 unsigned short level;
665 { "peon", "Peon", UL_PEON, '+' },
666 { "op", "Op", UL_OP, '@' },
667 { "master", "Master", UL_MASTER, '%' },
668 { "coowner", "Coowner", UL_COOWNER, '*' },
669 { "owner", "Owner", UL_OWNER, '!' },
670 { "helper", "BUG:", UL_HELPER, 'X' }
673 static const struct {
676 unsigned short default_value;
677 unsigned int old_idx;
678 unsigned int old_flag;
679 unsigned short flag_value;
681 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
682 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
683 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
684 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
685 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
686 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
687 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
688 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
689 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
690 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
691 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
692 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
695 struct charOptionValues {
698 } protectValues[] = {
699 { 'a', "CSMSG_PROTECT_ALL" },
700 { 'e', "CSMSG_PROTECT_EQUAL" },
701 { 'l', "CSMSG_PROTECT_LOWER" },
702 { 'n', "CSMSG_PROTECT_NONE" }
704 { 'd', "CSMSG_TOYS_DISABLED" },
705 { 'n', "CSMSG_TOYS_PRIVATE" },
706 { 'p', "CSMSG_TOYS_PUBLIC" }
707 }, topicRefreshValues[] = {
708 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
709 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
710 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
711 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
712 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
713 }, ctcpReactionValues[] = {
714 { 'k', "CSMSG_CTCPREACTION_KICK" },
715 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
716 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
717 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
720 static const struct {
724 unsigned int old_idx;
726 struct charOptionValues *values;
728 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
729 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
730 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
731 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
734 struct userData *helperList;
735 struct chanData *channelList;
736 static struct module *chanserv_module;
737 static unsigned int userCount;
739 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
740 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
741 static void unregister_channel(struct chanData *channel, const char *reason);
744 user_level_from_name(const char *name, unsigned short clamp_level)
746 unsigned int level = 0, ii;
748 level = strtoul(name, NULL, 10);
749 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
750 if(!irccasecmp(name, accessLevels[ii].name))
751 level = accessLevels[ii].level;
752 if(level > clamp_level)
758 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
761 *minl = strtoul(arg, &sep, 10);
769 *maxl = strtoul(sep+1, &sep, 10);
777 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
779 struct userData *uData, **head;
781 if(!channel || !handle)
784 if(override && HANDLE_FLAGGED(handle, HELPING)
785 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
787 for(uData = helperList;
788 uData && uData->handle != handle;
789 uData = uData->next);
793 uData = calloc(1, sizeof(struct userData));
794 uData->handle = handle;
796 uData->access = UL_HELPER;
802 uData->next = helperList;
804 helperList->prev = uData;
812 for(uData = channel->users; uData; uData = uData->next)
813 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
816 head = &(channel->users);
819 if(uData && (uData != *head))
821 /* Shuffle the user to the head of whatever list he was in. */
823 uData->next->prev = uData->prev;
825 uData->prev->next = uData->next;
831 (**head).prev = uData;
838 /* Returns non-zero if user has at least the minimum access.
839 * exempt_owner is set when handling !set, so the owner can set things
842 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
844 struct userData *uData;
845 struct chanData *cData = channel->channel_info;
846 unsigned short minimum = cData->lvlOpts[opt];
849 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
852 if(minimum <= uData->access)
854 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
859 /* Scan for other users authenticated to the same handle
860 still in the channel. If so, keep them listed as present.
862 user is optional, if not null, it skips checking that userNode
863 (for the handle_part function) */
865 scan_user_presence(struct userData *uData, struct userNode *user)
869 if(IsSuspended(uData->channel)
870 || IsUserSuspended(uData)
871 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
883 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
885 unsigned int eflags, argc;
887 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
889 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
890 if(!channel->channel_info
891 || IsSuspended(channel->channel_info)
893 || !ircncasecmp(text, "ACTION ", 7))
895 /* Figure out the minimum level needed to CTCP the channel */
896 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
898 /* We need to enforce against them; do so. */
900 argv[0] = (char*)text;
901 argv[1] = user->nick;
903 if(GetUserMode(channel, user))
904 eflags |= ACTION_KICK;
905 switch(channel->channel_info->chOpts[chCTCPReaction]) {
906 default: case 'k': /* just do the kick */ break;
908 eflags |= ACTION_BAN;
911 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
912 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
915 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
916 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
919 argv[argc++] = bad_ctcp_reason;
920 eject_user(chanserv, channel, argc, argv, NULL, eflags);
924 chanserv_create_note_type(const char *name)
926 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
927 strcpy(ntype->name, name);
929 dict_insert(note_types, ntype->name, ntype);
934 free_vote_options(void *data)
936 struct vote_option *vOpt = data;
938 free(vOpt->option_str);
943 chanserv_deref_note_type(void *data)
945 struct note_type *ntype = data;
947 if(--ntype->refs > 0)
953 chanserv_flush_note_type(struct note_type *ntype)
955 struct chanData *cData;
956 for(cData = channelList; cData; cData = cData->next)
957 dict_remove(cData->notes, ntype->name);
961 chanserv_truncate_notes(struct note_type *ntype)
963 struct chanData *cData;
965 unsigned int size = sizeof(*note) + ntype->max_length;
967 for(cData = channelList; cData; cData = cData->next) {
968 note = dict_find(cData->notes, ntype->name, NULL);
971 if(strlen(note->note) <= ntype->max_length)
973 dict_remove2(cData->notes, ntype->name, 1);
974 note = realloc(note, size);
975 note->note[ntype->max_length] = 0;
976 dict_insert(cData->notes, ntype->name, note);
980 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
983 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
986 unsigned int len = strlen(text);
988 if(len > type->max_length) len = type->max_length;
989 note = calloc(1, sizeof(*note) + len);
991 strncpy(note->setter, setter, sizeof(note->setter)-1);
992 memcpy(note->note, text, len);
994 dict_insert(channel->notes, type->name, note);
1000 chanserv_free_note(void *data)
1002 struct note *note = data;
1004 chanserv_deref_note_type(note->type);
1005 assert(note->type->refs > 0); /* must use delnote to remove the type */
1009 static MODCMD_FUNC(cmd_createnote) {
1010 struct note_type *ntype;
1011 unsigned int arg = 1, existed = 0, max_length;
1013 if((ntype = dict_find(note_types, argv[1], NULL)))
1016 ntype = chanserv_create_note_type(argv[arg]);
1017 if(!irccasecmp(argv[++arg], "privileged"))
1020 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1021 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1023 else if(!irccasecmp(argv[arg], "channel"))
1025 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1028 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1031 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1032 ntype->set_access.min_ulevel = ulvl;
1034 else if(!irccasecmp(argv[arg], "setter"))
1036 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1040 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1044 if(!irccasecmp(argv[++arg], "privileged"))
1045 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1046 else if(!irccasecmp(argv[arg], "channel_users"))
1047 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1048 else if(!irccasecmp(argv[arg], "all"))
1049 ntype->visible_type = NOTE_VIS_ALL;
1051 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1055 if((arg+1) >= argc) {
1056 reply("MSG_MISSING_PARAMS", argv[0]);
1059 max_length = strtoul(argv[++arg], NULL, 0);
1060 if(max_length < 20 || max_length > 450)
1062 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1065 if(existed && (max_length < ntype->max_length))
1067 ntype->max_length = max_length;
1068 chanserv_truncate_notes(ntype);
1070 ntype->max_length = max_length;
1073 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1075 reply("CSMSG_NOTE_CREATED", ntype->name);
1080 dict_remove(note_types, ntype->name);
1084 static MODCMD_FUNC(cmd_removenote) {
1085 struct note_type *ntype;
1088 ntype = dict_find(note_types, argv[1], NULL);
1089 force = (argc > 2) && !irccasecmp(argv[2], "force");
1092 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1099 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1102 chanserv_flush_note_type(ntype);
1104 dict_remove(note_types, argv[1]);
1105 reply("CSMSG_NOTE_DELETED", argv[1]);
1110 chanserv_expire_channel(void *data)
1112 struct chanData *channel = data;
1113 char reason[MAXLEN];
1114 sprintf(reason, "channel expired.");
1115 channel->expiry = 0;
1116 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1117 unregister_channel(channel, reason);
1120 static MODCMD_FUNC(chan_opt_expire)
1122 struct chanData *cData = channel->channel_info;
1123 unsigned long value = cData->expiry;
1127 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1129 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1132 unsigned long expiry,duration;
1134 /* The two directions can have different ACLs. */
1135 if(!strcmp(argv[1], "0"))
1137 else if((duration = ParseInterval(argv[1])))
1138 expiry = now + duration;
1141 reply("MSG_INVALID_DURATION", argv[1]);
1145 if (expiry != value)
1149 timeq_del(value, chanserv_expire_channel, cData, 0);
1152 cData->expiry = value;
1155 timeq_add(expiry, chanserv_expire_channel, cData);
1160 if(cData->expiry > now) {
1161 char expirestr[INTERVALLEN];
1162 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1164 reply("CSMSG_SET_EXPIRE_OFF");
1169 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1173 if(orig->modes_set & change->modes_clear)
1175 if(orig->modes_clear & change->modes_set)
1177 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1178 && strcmp(orig->new_key, change->new_key))
1180 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1181 && (orig->new_limit != change->new_limit))
1186 static char max_length_text[MAXLEN+1][16];
1188 static struct helpfile_expansion
1189 chanserv_expand_variable(const char *variable)
1191 struct helpfile_expansion exp;
1193 if(!irccasecmp(variable, "notes"))
1196 exp.type = HF_TABLE;
1197 exp.value.table.length = 1;
1198 exp.value.table.width = 3;
1199 exp.value.table.flags = 0;
1200 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1201 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1202 exp.value.table.contents[0][0] = "Note Type";
1203 exp.value.table.contents[0][1] = "Visibility";
1204 exp.value.table.contents[0][2] = "Max Length";
1205 for(it=dict_first(note_types); it; it=iter_next(it))
1207 struct note_type *ntype = iter_data(it);
1210 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1211 row = exp.value.table.length++;
1212 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1213 exp.value.table.contents[row][0] = ntype->name;
1214 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1215 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1217 if(!max_length_text[ntype->max_length][0])
1218 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1219 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1224 exp.type = HF_STRING;
1225 exp.value.str = NULL;
1229 static struct chanData*
1230 register_channel(struct chanNode *cNode, char *registrar)
1232 struct chanData *channel;
1233 enum levelOption lvlOpt;
1234 enum charOption chOpt;
1237 channel = calloc(1, sizeof(struct chanData));
1239 channel->notes = dict_new();
1240 dict_set_free_data(channel->notes, chanserv_free_note);
1242 channel->registrar = strdup(registrar);
1243 channel->registered = now;
1244 channel->visited = now;
1245 channel->limitAdjusted = now;
1246 channel->ownerTransfer = now;
1247 channel->flags = CHANNEL_DEFAULT_FLAGS;
1248 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1249 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1250 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1251 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1252 for(i = 0; i < MAXADVTOPICENTRIES; i++)
1253 channel->advtopic[i] = NULL;
1255 channel->prev = NULL;
1256 channel->next = channelList;
1259 channelList->prev = channel;
1260 channelList = channel;
1261 registered_channels++;
1263 channel->channel = cNode;
1265 cNode->channel_info = channel;
1267 channel->vote = NULL;
1272 static struct userData*
1273 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1275 struct userData *ud;
1277 if(access_level > UL_OWNER)
1280 ud = calloc(1, sizeof(*ud));
1281 ud->channel = channel;
1282 ud->handle = handle;
1284 ud->access = access_level;
1285 ud->info = info ? strdup(info) : NULL;
1288 ud->next = channel->users;
1290 channel->users->prev = ud;
1291 channel->users = ud;
1293 channel->userCount++;
1297 ud->u_next = ud->handle->channels;
1299 ud->u_next->u_prev = ud;
1300 ud->handle->channels = ud;
1306 del_channel_user(struct userData *user, int do_gc)
1308 struct chanData *channel = user->channel;
1310 channel->userCount--;
1314 user->prev->next = user->next;
1316 channel->users = user->next;
1318 user->next->prev = user->prev;
1321 user->u_prev->u_next = user->u_next;
1323 user->handle->channels = user->u_next;
1325 user->u_next->u_prev = user->u_prev;
1329 if(do_gc && !channel->users && !IsProtected(channel)) {
1330 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1331 unregister_channel(channel, "lost all users.");
1335 static void expire_ban(void *data);
1338 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1341 unsigned int ii, l1, l2;
1346 bd = malloc(sizeof(struct banData));
1348 bd->channel = channel;
1350 bd->triggered = triggered;
1351 bd->expires = expires;
1353 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1355 extern const char *hidden_host_suffix;
1356 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1360 l2 = strlen(old_name);
1363 if(irccasecmp(mask + l1 - l2, old_name))
1365 new_mask = alloca(MAXLEN);
1366 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1369 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1371 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1372 bd->reason = strdup(reason);
1375 timeq_add(expires, expire_ban, bd);
1378 bd->next = channel->bans;
1380 channel->bans->prev = bd;
1382 channel->banCount++;
1389 del_channel_ban(struct banData *ban)
1391 ban->channel->banCount--;
1395 ban->prev->next = ban->next;
1397 ban->channel->bans = ban->next;
1400 ban->next->prev = ban->prev;
1403 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1412 expire_ban(void *data)
1414 struct banData *bd = data;
1415 if(!IsSuspended(bd->channel))
1417 struct banList bans;
1418 struct mod_chanmode change;
1420 bans = bd->channel->channel->banlist;
1421 mod_chanmode_init(&change);
1422 for(ii=0; ii<bans.used; ii++)
1424 if(!strcmp(bans.list[ii]->ban, bd->mask))
1427 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1428 change.args[0].u.hostmask = bd->mask;
1429 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1435 del_channel_ban(bd);
1438 static void chanserv_expire_suspension(void *data);
1441 unregister_channel(struct chanData *channel, const char *reason)
1443 struct mod_chanmode change;
1444 char msgbuf[MAXLEN];
1447 /* After channel unregistration, the following must be cleaned
1449 - Channel information.
1452 - Channel suspension data.
1453 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1459 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1461 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1462 mod_chanmode_init(&change);
1464 change.modes_clear |= MODE_REGISTERED;
1465 if(chanserv_conf.revoke_mode_a)
1466 change.modes_clear |= MODE_ACCESS;
1467 mod_chanmode_announce(chanserv, channel->channel, &change);
1470 while(channel->users)
1471 del_channel_user(channel->users, 0);
1473 while(channel->bans)
1474 del_channel_ban(channel->bans);
1476 free(channel->topic);
1477 free(channel->registrar);
1478 free(channel->greeting);
1479 free(channel->user_greeting);
1480 free(channel->topic_mask);
1482 for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1483 if(channel->advtopic[i])
1484 free(channel->advtopic[i]);
1488 channel->prev->next = channel->next;
1490 channelList = channel->next;
1493 channel->next->prev = channel->prev;
1495 if(channel->suspended)
1497 struct chanNode *cNode = channel->channel;
1498 struct suspended *suspended, *next_suspended;
1500 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1502 next_suspended = suspended->previous;
1503 free(suspended->suspender);
1504 free(suspended->reason);
1505 if(suspended->expires)
1506 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1511 cNode->channel_info = NULL;
1514 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1515 channel->channel->channel_info = NULL;
1517 dict_delete(channel->notes);
1518 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1519 if(!IsSuspended(channel))
1520 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1521 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1522 UnlockChannel(channel->channel);
1524 registered_channels--;
1528 expire_channels(void *data)
1530 struct chanData *channel, *next;
1531 struct userData *user;
1532 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1534 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1535 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1537 for(channel = channelList; channel; channel = next)
1539 next = channel->next;
1541 /* See if the channel can be expired. */
1542 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1543 || IsProtected(channel))
1546 /* Make sure there are no high-ranking users still in the channel. */
1547 for(user=channel->users; user; user=user->next)
1548 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1553 /* Unregister the channel */
1554 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1555 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1556 unregister_channel(channel, "registration expired.");
1559 if(chanserv_conf.channel_expire_frequency && !data)
1560 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1564 expire_dnrs(UNUSED_ARG(void *data))
1566 dict_iterator_t it, next;
1567 struct do_not_register *dnr;
1569 for(it = dict_first(handle_dnrs); it; it = next)
1571 dnr = iter_data(it);
1572 next = iter_next(it);
1573 if(dnr->expires && dnr->expires <= now)
1574 dict_remove(handle_dnrs, dnr->chan_name + 1);
1576 for(it = dict_first(plain_dnrs); it; it = next)
1578 dnr = iter_data(it);
1579 next = iter_next(it);
1580 if(dnr->expires && dnr->expires <= now)
1581 dict_remove(plain_dnrs, dnr->chan_name + 1);
1583 for(it = dict_first(mask_dnrs); it; it = next)
1585 dnr = iter_data(it);
1586 next = iter_next(it);
1587 if(dnr->expires && dnr->expires <= now)
1588 dict_remove(mask_dnrs, dnr->chan_name + 1);
1591 if(chanserv_conf.dnr_expire_frequency)
1592 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1596 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1598 char protect = channel->chOpts[chProtect];
1599 struct userData *cs_victim, *cs_aggressor;
1601 /* Don't protect if no one is to be protected, someone is attacking
1602 himself, or if the aggressor is an IRC Operator. */
1603 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1606 /* Don't protect if the victim isn't authenticated (because they
1607 can't be a channel user), unless we are to protect non-users
1609 cs_victim = GetChannelAccess(channel, victim->handle_info);
1610 if(protect != 'a' && !cs_victim)
1613 /* Protect if the aggressor isn't a user because at this point,
1614 the aggressor can only be less than or equal to the victim. */
1615 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1619 /* If the aggressor was a user, then the victim can't be helped. */
1626 if(cs_victim->access > cs_aggressor->access)
1631 if(cs_victim->access >= cs_aggressor->access)
1640 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1642 struct chanData *cData = channel->channel_info;
1643 struct userData *cs_victim;
1645 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1646 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1647 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1649 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1657 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1659 if(IsService(victim))
1661 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1665 if(protect_user(victim, user, channel->channel_info))
1667 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1674 static struct do_not_register *
1675 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1677 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1678 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1679 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1680 strcpy(dnr->reason, reason);
1682 dnr->expires = expires;
1683 if(dnr->chan_name[0] == '*')
1684 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1685 else if(strpbrk(dnr->chan_name, "*?"))
1686 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1688 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1692 static struct dnrList
1693 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1695 struct dnrList list;
1696 dict_iterator_t it, next;
1697 struct do_not_register *dnr;
1699 dnrList_init(&list);
1701 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1703 if(dnr->expires && dnr->expires <= now)
1704 dict_remove(handle_dnrs, handle);
1705 else if(list.used < max)
1706 dnrList_append(&list, dnr);
1709 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1711 if(dnr->expires && dnr->expires <= now)
1712 dict_remove(plain_dnrs, chan_name);
1713 else if(list.used < max)
1714 dnrList_append(&list, dnr);
1719 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1721 next = iter_next(it);
1722 if(!match_ircglob(chan_name, iter_key(it)))
1724 dnr = iter_data(it);
1725 if(dnr->expires && dnr->expires <= now)
1726 dict_remove(mask_dnrs, iter_key(it));
1728 dnrList_append(&list, dnr);
1735 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1737 struct userNode *user;
1738 char buf1[INTERVALLEN];
1739 char buf2[INTERVALLEN];
1746 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1751 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1752 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1756 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1759 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1764 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1766 struct dnrList list;
1769 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1770 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1771 dnr_print_func(list.list[ii], user);
1773 reply("CSMSG_MORE_DNRS", list.used - ii);
1778 struct do_not_register *
1779 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1781 struct dnrList list;
1782 struct do_not_register *dnr;
1784 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1785 dnr = list.used ? list.list[0] : NULL;
1790 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1792 struct do_not_register *dnr;
1793 dict_iterator_t it, next;
1794 unsigned int matches = 0;
1796 for(it = dict_first(dict); it; it = next)
1798 dnr = iter_data(it);
1799 next = iter_next(it);
1800 if(dnr->expires && dnr->expires <= now)
1802 dict_remove(dict, iter_key(it));
1805 dnr_print_func(dnr, user);
1812 static CHANSERV_FUNC(cmd_noregister)
1816 unsigned long expiry, duration;
1817 unsigned int matches;
1821 reply("CSMSG_DNR_SEARCH_RESULTS");
1822 matches = send_dnrs(user, handle_dnrs);
1823 matches += send_dnrs(user, plain_dnrs);
1824 matches += send_dnrs(user, mask_dnrs);
1826 reply("MSG_MATCH_COUNT", matches);
1828 reply("MSG_NO_MATCHES");
1834 if(!IsChannelName(target) && (*target != '*'))
1836 reply("CSMSG_NOT_DNR", target);
1844 reply("MSG_INVALID_DURATION", argv[2]);
1848 if(!strcmp(argv[2], "0"))
1850 else if((duration = ParseInterval(argv[2])))
1851 expiry = now + duration;
1854 reply("MSG_INVALID_DURATION", argv[2]);
1858 reason = unsplit_string(argv + 3, argc - 3, NULL);
1859 if((*target == '*') && !get_handle_info(target + 1))
1861 reply("MSG_HANDLE_UNKNOWN", target + 1);
1864 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1865 reply("CSMSG_NOREGISTER_CHANNEL", target);
1869 reply("CSMSG_DNR_SEARCH_RESULTS");
1871 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1873 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1875 reply("MSG_NO_MATCHES");
1879 static CHANSERV_FUNC(cmd_allowregister)
1881 const char *chan_name = argv[1];
1883 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1884 || dict_remove(plain_dnrs, chan_name)
1885 || dict_remove(mask_dnrs, chan_name))
1887 reply("CSMSG_DNR_REMOVED", chan_name);
1890 reply("CSMSG_NO_SUCH_DNR", chan_name);
1895 struct userNode *source;
1899 unsigned long min_set, max_set;
1900 unsigned long min_expires, max_expires;
1905 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1907 return !((dnr->set < search->min_set)
1908 || (dnr->set > search->max_set)
1909 || (dnr->expires < search->min_expires)
1910 || (search->max_expires
1911 && ((dnr->expires == 0)
1912 || (dnr->expires > search->max_expires)))
1913 || (search->chan_mask
1914 && !match_ircglob(dnr->chan_name, search->chan_mask))
1915 || (search->setter_mask
1916 && !match_ircglob(dnr->setter, search->setter_mask))
1917 || (search->reason_mask
1918 && !match_ircglob(dnr->reason, search->reason_mask)));
1921 static struct dnr_search *
1922 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1924 struct dnr_search *discrim;
1927 discrim = calloc(1, sizeof(*discrim));
1928 discrim->source = user;
1929 discrim->chan_mask = NULL;
1930 discrim->setter_mask = NULL;
1931 discrim->reason_mask = NULL;
1932 discrim->max_set = INT_MAX;
1933 discrim->limit = 50;
1935 for(ii=0; ii<argc; ++ii)
1939 reply("MSG_MISSING_PARAMS", argv[ii]);
1942 else if(0 == irccasecmp(argv[ii], "channel"))
1944 discrim->chan_mask = argv[++ii];
1946 else if(0 == irccasecmp(argv[ii], "setter"))
1948 discrim->setter_mask = argv[++ii];
1950 else if(0 == irccasecmp(argv[ii], "reason"))
1952 discrim->reason_mask = argv[++ii];
1954 else if(0 == irccasecmp(argv[ii], "limit"))
1956 discrim->limit = strtoul(argv[++ii], NULL, 0);
1958 else if(0 == irccasecmp(argv[ii], "set"))
1960 const char *cmp = argv[++ii];
1963 discrim->min_set = now - ParseInterval(cmp + 2);
1965 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1966 } else if(cmp[0] == '=') {
1967 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1968 } else if(cmp[0] == '>') {
1970 discrim->max_set = now - ParseInterval(cmp + 2);
1972 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1974 discrim->max_set = now - (ParseInterval(cmp) - 1);
1977 else if(0 == irccasecmp(argv[ii], "expires"))
1979 const char *cmp = argv[++ii];
1982 discrim->max_expires = now + ParseInterval(cmp + 2);
1984 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1985 } else if(cmp[0] == '=') {
1986 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1987 } else if(cmp[0] == '>') {
1989 discrim->min_expires = now + ParseInterval(cmp + 2);
1991 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1993 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1998 reply("MSG_INVALID_CRITERIA", argv[ii]);
2009 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2012 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2014 struct do_not_register *dnr;
2015 dict_iterator_t next;
2020 /* Initialize local variables. */
2023 if(discrim->chan_mask)
2025 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2026 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2030 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2032 /* Check against account-based DNRs. */
2033 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2034 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2037 else if(target_fixed)
2039 /* Check against channel-based DNRs. */
2040 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2041 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2046 /* Exhaustively search account DNRs. */
2047 for(it = dict_first(handle_dnrs); it; it = next)
2049 next = iter_next(it);
2050 dnr = iter_data(it);
2051 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2055 /* Do the same for channel DNRs. */
2056 for(it = dict_first(plain_dnrs); it; it = next)
2058 next = iter_next(it);
2059 dnr = iter_data(it);
2060 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2064 /* Do the same for wildcarded channel DNRs. */
2065 for(it = dict_first(mask_dnrs); it; it = next)
2067 next = iter_next(it);
2068 dnr = iter_data(it);
2069 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2077 dnr_remove_func(struct do_not_register *match, void *extra)
2079 struct userNode *user;
2082 chan_name = alloca(strlen(match->chan_name) + 1);
2083 strcpy(chan_name, match->chan_name);
2085 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2086 || dict_remove(plain_dnrs, chan_name)
2087 || dict_remove(mask_dnrs, chan_name))
2089 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2095 dnr_count_func(struct do_not_register *match, void *extra)
2097 return 0; (void)match; (void)extra;
2100 static MODCMD_FUNC(cmd_dnrsearch)
2102 struct dnr_search *discrim;
2103 dnr_search_func action;
2104 struct svccmd *subcmd;
2105 unsigned int matches;
2108 sprintf(buf, "dnrsearch %s", argv[1]);
2109 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2112 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2115 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2117 if(!irccasecmp(argv[1], "print"))
2118 action = dnr_print_func;
2119 else if(!irccasecmp(argv[1], "remove"))
2120 action = dnr_remove_func;
2121 else if(!irccasecmp(argv[1], "count"))
2122 action = dnr_count_func;
2125 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2129 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2133 if(action == dnr_print_func)
2134 reply("CSMSG_DNR_SEARCH_RESULTS");
2135 matches = dnr_search(discrim, action, user);
2137 reply("MSG_MATCH_COUNT", matches);
2139 reply("MSG_NO_MATCHES");
2145 chanserv_get_owned_count(struct handle_info *hi)
2147 struct userData *cList;
2150 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2151 if(cList->access == UL_OWNER)
2156 static CHANSERV_FUNC(cmd_register)
2158 struct handle_info *handle;
2159 struct chanData *cData;
2160 struct modeNode *mn;
2161 char reason[MAXLEN];
2163 unsigned int new_channel, force=0;
2164 struct do_not_register *dnr;
2168 if(channel->channel_info)
2170 reply("CSMSG_ALREADY_REGGED", channel->name);
2174 if(channel->bad_channel)
2176 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2181 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2183 reply("CSMSG_MUST_BE_OPPED", channel->name);
2188 chan_name = channel->name;
2192 if((argc < 2) || !IsChannelName(argv[1]))
2194 reply("MSG_NOT_CHANNEL_NAME");
2198 if(opserv_bad_channel(argv[1]))
2200 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2205 chan_name = argv[1];
2208 if(argc >= (new_channel+2))
2210 if(!IsHelping(user))
2212 reply("CSMSG_PROXY_FORBIDDEN");
2216 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2218 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2219 dnr = chanserv_is_dnr(chan_name, handle);
2223 handle = user->handle_info;
2224 dnr = chanserv_is_dnr(chan_name, handle);
2228 if(!IsHelping(user))
2229 reply("CSMSG_DNR_CHANNEL", chan_name);
2231 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2235 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2237 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2242 channel = AddChannel(argv[1], now, NULL, NULL);
2244 cData = register_channel(channel, user->handle_info->handle);
2245 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2246 cData->modes = chanserv_conf.default_modes;
2248 cData->modes.modes_set |= MODE_REGISTERED;
2249 if (IsOffChannel(cData))
2251 mod_chanmode_announce(chanserv, channel, &cData->modes);
2255 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2256 change->args[change->argc].mode = MODE_CHANOP;
2257 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2259 mod_chanmode_announce(chanserv, channel, change);
2260 mod_chanmode_free(change);
2263 /* Initialize the channel's max user record. */
2264 cData->max = channel->members.used;
2265 cData->max_time = 0;
2267 if(handle != user->handle_info)
2268 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2270 reply("CSMSG_REG_SUCCESS", channel->name);
2272 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2273 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2278 make_confirmation_string(struct userData *uData)
2280 static char strbuf[16];
2285 for(src = uData->handle->handle; *src; )
2286 accum = accum * 31 + toupper(*src++);
2288 for(src = uData->channel->channel->name; *src; )
2289 accum = accum * 31 + toupper(*src++);
2290 sprintf(strbuf, "%08x", accum);
2294 static CHANSERV_FUNC(cmd_unregister)
2297 char reason[MAXLEN];
2298 struct chanData *cData;
2299 struct userData *uData;
2301 cData = channel->channel_info;
2304 reply("CSMSG_NOT_REGISTERED", channel->name);
2308 uData = GetChannelUser(cData, user->handle_info);
2309 if(!uData || (uData->access < UL_OWNER))
2311 reply("CSMSG_NO_ACCESS");
2315 if(IsProtected(cData) && !IsOper(user))
2317 reply("CSMSG_UNREG_NODELETE", channel->name);
2321 if(!IsHelping(user))
2323 const char *confirm_string;
2324 if(IsSuspended(cData))
2326 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2329 confirm_string = make_confirmation_string(uData);
2330 if((argc < 2) || strcmp(argv[1], confirm_string))
2332 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2337 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2338 name = strdup(channel->name);
2339 unregister_channel(cData, reason);
2340 spamserv_cs_unregister(user, channel, manually, "unregistered");
2341 reply("CSMSG_UNREG_SUCCESS", name);
2347 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2349 extern struct userNode *spamserv;
2350 struct mod_chanmode *change;
2352 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2354 change = mod_chanmode_alloc(2);
2356 change->args[0].mode = MODE_CHANOP;
2357 change->args[0].u.member = AddChannelUser(chanserv, channel);
2358 change->args[1].mode = MODE_CHANOP;
2359 change->args[1].u.member = AddChannelUser(spamserv, channel);
2363 change = mod_chanmode_alloc(1);
2365 change->args[0].mode = MODE_CHANOP;
2366 change->args[0].u.member = AddChannelUser(chanserv, channel);
2369 mod_chanmode_announce(chanserv, channel, change);
2370 mod_chanmode_free(change);
2373 static CHANSERV_FUNC(cmd_move)
2375 struct mod_chanmode change;
2376 struct chanNode *target;
2377 struct modeNode *mn;
2378 struct userData *uData;
2379 char reason[MAXLEN];
2380 struct do_not_register *dnr;
2381 int chanserv_join = 0, spamserv_join;
2385 if(IsProtected(channel->channel_info))
2387 reply("CSMSG_MOVE_NODELETE", channel->name);
2391 if(!IsChannelName(argv[1]))
2393 reply("MSG_NOT_CHANNEL_NAME");
2397 if(opserv_bad_channel(argv[1]))
2399 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2403 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2405 for(uData = channel->channel_info->users; uData; uData = uData->next)
2407 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2409 if(!IsHelping(user))
2410 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2412 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2418 mod_chanmode_init(&change);
2419 if(!(target = GetChannel(argv[1])))
2421 target = AddChannel(argv[1], now, NULL, NULL);
2422 if(!IsSuspended(channel->channel_info))
2425 else if(target->channel_info)
2427 reply("CSMSG_ALREADY_REGGED", target->name);
2430 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2431 && !IsHelping(user))
2433 reply("CSMSG_MUST_BE_OPPED", target->name);
2436 else if(!IsSuspended(channel->channel_info))
2441 /* Clear MODE_REGISTERED from old channel, add it to new. */
2443 change.modes_clear = MODE_REGISTERED;
2444 mod_chanmode_announce(chanserv, channel, &change);
2445 change.modes_clear = 0;
2446 change.modes_set = MODE_REGISTERED;
2447 mod_chanmode_announce(chanserv, target, &change);
2450 /* Move the channel_info to the target channel; it
2451 shouldn't be necessary to clear timeq callbacks
2452 for the old channel. */
2453 target->channel_info = channel->channel_info;
2454 target->channel_info->channel = target;
2455 channel->channel_info = NULL;
2457 /* Check whether users are present in the new channel. */
2458 for(uData = target->channel_info->users; uData; uData = uData->next)
2459 scan_user_presence(uData, NULL);
2461 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2464 ss_cs_join_channel(target, spamserv_join);
2466 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2467 if(!IsSuspended(target->channel_info))
2469 char reason2[MAXLEN];
2470 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2471 DelChannelUser(chanserv, channel, reason2, 0);
2473 UnlockChannel(channel);
2474 LockChannel(target);
2475 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2476 reply("CSMSG_MOVE_SUCCESS", target->name);
2481 merge_users(struct chanData *source, struct chanData *target)
2483 struct userData *suData, *tuData, *next;
2489 /* Insert the source's users into the scratch area. */
2490 for(suData = source->users; suData; suData = suData->next)
2491 dict_insert(merge, suData->handle->handle, suData);
2493 /* Iterate through the target's users, looking for
2494 users common to both channels. The lower access is
2495 removed from either the scratch area or target user
2497 for(tuData = target->users; tuData; tuData = next)
2499 struct userData *choice;
2501 next = tuData->next;
2503 /* If a source user exists with the same handle as a target
2504 channel's user, resolve the conflict by removing one. */
2505 suData = dict_find(merge, tuData->handle->handle, NULL);
2509 /* Pick the data we want to keep. */
2510 /* If the access is the same, use the later seen time. */
2511 if(suData->access == tuData->access)
2512 choice = (suData->seen > tuData->seen) ? suData : tuData;
2513 else /* Otherwise, keep the higher access level. */
2514 choice = (suData->access > tuData->access) ? suData : tuData;
2515 /* Use the later seen time. */
2516 if(suData->seen < tuData->seen)
2517 suData->seen = tuData->seen;
2519 tuData->seen = suData->seen;
2521 /* Remove the user that wasn't picked. */
2522 if(choice == tuData)
2524 dict_remove(merge, suData->handle->handle);
2525 del_channel_user(suData, 0);
2528 del_channel_user(tuData, 0);
2531 /* Move the remaining users to the target channel. */
2532 for(it = dict_first(merge); it; it = iter_next(it))
2534 suData = iter_data(it);
2536 /* Insert the user into the target channel's linked list. */
2537 suData->prev = NULL;
2538 suData->next = target->users;
2539 suData->channel = target;
2542 target->users->prev = suData;
2543 target->users = suData;
2545 /* Update the user counts for the target channel; the
2546 source counts are left alone. */
2547 target->userCount++;
2549 /* Check whether the user is in the target channel. */
2550 scan_user_presence(suData, NULL);
2553 /* Possible to assert (source->users == NULL) here. */
2554 source->users = NULL;
2559 merge_bans(struct chanData *source, struct chanData *target)
2561 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2563 /* Hold on to the original head of the target ban list
2564 to avoid comparing source bans with source bans. */
2565 tFront = target->bans;
2567 /* Perform a totally expensive O(n*m) merge, ick. */
2568 for(sbData = source->bans; sbData; sbData = sNext)
2570 /* Flag to track whether the ban's been moved
2571 to the destination yet. */
2574 /* Possible to assert (sbData->prev == NULL) here. */
2575 sNext = sbData->next;
2577 for(tbData = tFront; tbData; tbData = tNext)
2579 tNext = tbData->next;
2581 /* Perform two comparisons between each source
2582 and target ban, conflicts are resolved by
2583 keeping the broader ban and copying the later
2584 expiration and triggered time. */
2585 if(match_ircglobs(tbData->mask, sbData->mask))
2587 /* There is a broader ban in the target channel that
2588 overrides one in the source channel; remove the
2589 source ban and break. */
2590 if(sbData->expires > tbData->expires)
2591 tbData->expires = sbData->expires;
2592 if(sbData->triggered > tbData->triggered)
2593 tbData->triggered = sbData->triggered;
2594 del_channel_ban(sbData);
2597 else if(match_ircglobs(sbData->mask, tbData->mask))
2599 /* There is a broader ban in the source channel that
2600 overrides one in the target channel; remove the
2601 target ban, fall through and move the source over. */
2602 if(tbData->expires > sbData->expires)
2603 sbData->expires = tbData->expires;
2604 if(tbData->triggered > sbData->triggered)
2605 sbData->triggered = tbData->triggered;
2606 if(tbData == tFront)
2608 del_channel_ban(tbData);
2611 /* Source bans can override multiple target bans, so
2612 we allow a source to run through this loop multiple
2613 times, but we can only move it once. */
2618 /* Remove the source ban from the source ban list. */
2620 sbData->next->prev = sbData->prev;
2622 /* Modify the source ban's associated channel. */
2623 sbData->channel = target;
2625 /* Insert the ban into the target channel's linked list. */
2626 sbData->prev = NULL;
2627 sbData->next = target->bans;
2630 target->bans->prev = sbData;
2631 target->bans = sbData;
2633 /* Update the user counts for the target channel. */
2638 /* Possible to assert (source->bans == NULL) here. */
2639 source->bans = NULL;
2643 merge_data(struct chanData *source, struct chanData *target)
2645 /* Use more recent visited and owner-transfer time; use older
2646 * registered time. Bitwise or may_opchan. Use higher max.
2647 * Do not touch last_refresh, ban count or user counts.
2649 if(source->visited > target->visited)
2650 target->visited = source->visited;
2651 if(source->registered < target->registered)
2652 target->registered = source->registered;
2653 if(source->ownerTransfer > target->ownerTransfer)
2654 target->ownerTransfer = source->ownerTransfer;
2655 if(source->may_opchan)
2656 target->may_opchan = 1;
2657 if(source->max > target->max) {
2658 target->max = source->max;
2659 target->max_time = source->max_time;
2664 merge_channel(struct chanData *source, struct chanData *target)
2666 merge_users(source, target);
2667 merge_bans(source, target);
2668 merge_data(source, target);
2671 static CHANSERV_FUNC(cmd_merge)
2673 struct userData *target_user;
2674 struct chanNode *target;
2675 char reason[MAXLEN];
2679 /* Make sure the target channel exists and is registered to the user
2680 performing the command. */
2681 if(!(target = GetChannel(argv[1])))
2683 reply("MSG_INVALID_CHANNEL");
2687 if(!target->channel_info)
2689 reply("CSMSG_NOT_REGISTERED", target->name);
2693 if(IsProtected(channel->channel_info))
2695 reply("CSMSG_MERGE_NODELETE");
2699 if(IsSuspended(target->channel_info))
2701 reply("CSMSG_MERGE_SUSPENDED");
2705 if(channel == target)
2707 reply("CSMSG_MERGE_SELF");
2711 target_user = GetChannelUser(target->channel_info, user->handle_info);
2712 if(!target_user || (target_user->access < UL_OWNER))
2714 reply("CSMSG_MERGE_NOT_OWNER");
2718 /* Merge the channel structures and associated data. */
2719 merge_channel(channel->channel_info, target->channel_info);
2720 spamserv_cs_move_merge(user, channel, target, 0);
2721 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2722 unregister_channel(channel->channel_info, reason);
2723 reply("CSMSG_MERGE_SUCCESS", target->name);
2727 static CHANSERV_FUNC(cmd_opchan)
2729 struct mod_chanmode change;
2730 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2732 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2735 channel->channel_info->may_opchan = 0;
2736 mod_chanmode_init(&change);
2738 change.args[0].mode = MODE_CHANOP;
2739 change.args[0].u.member = GetUserMode(channel, chanserv);
2740 if(!change.args[0].u.member)
2742 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2745 mod_chanmode_announce(chanserv, channel, &change);
2746 reply("CSMSG_OPCHAN_DONE", channel->name);
2750 static CHANSERV_FUNC(cmd_adduser)
2752 struct userData *actee;
2753 struct userData *actor, *real_actor;
2754 struct handle_info *handle;
2755 unsigned short access_level, override = 0;
2759 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2761 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2765 access_level = user_level_from_name(argv[2], UL_OWNER);
2768 reply("CSMSG_INVALID_ACCESS", argv[2]);
2772 actor = GetChannelUser(channel->channel_info, user->handle_info);
2773 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2775 if(actor->access <= access_level)
2777 reply("CSMSG_NO_BUMP_ACCESS");
2781 /* Trying to add someone with equal/more access? */
2782 if (!real_actor || real_actor->access <= access_level)
2783 override = CMD_LOG_OVERRIDE;
2785 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2788 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2790 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2794 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2795 scan_user_presence(actee, NULL);
2796 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2797 return 1 | override;
2800 static CHANSERV_FUNC(cmd_clvl)
2802 struct handle_info *handle;
2803 struct userData *victim;
2804 struct userData *actor, *real_actor;
2805 unsigned short new_access, override = 0;
2806 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2810 actor = GetChannelUser(channel->channel_info, user->handle_info);
2811 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2813 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2816 if(handle == user->handle_info && !privileged)
2818 reply("CSMSG_NO_SELF_CLVL");
2822 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2824 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2828 if(actor->access <= victim->access && !privileged)
2830 reply("MSG_USER_OUTRANKED", handle->handle);
2834 new_access = user_level_from_name(argv[2], UL_OWNER);
2838 reply("CSMSG_INVALID_ACCESS", argv[2]);
2842 if(new_access >= actor->access && !privileged)
2844 reply("CSMSG_NO_BUMP_ACCESS");
2848 /* Trying to clvl a equal/higher user? */
2849 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2850 override = CMD_LOG_OVERRIDE;
2851 /* Trying to clvl someone to equal/higher access? */
2852 if(!real_actor || new_access >= real_actor->access)
2853 override = CMD_LOG_OVERRIDE;
2854 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2855 * If they lower their own access it's not a big problem.
2858 victim->access = new_access;
2859 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2860 return 1 | override;
2863 static CHANSERV_FUNC(cmd_deluser)
2865 struct handle_info *handle;
2866 struct userData *victim;
2867 struct userData *actor, *real_actor;
2868 unsigned short access_level, override = 0;
2873 actor = GetChannelUser(channel->channel_info, user->handle_info);
2874 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2876 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2879 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2881 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2887 access_level = user_level_from_name(argv[1], UL_OWNER);
2890 reply("CSMSG_INVALID_ACCESS", argv[1]);
2893 if(access_level != victim->access)
2895 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2901 access_level = victim->access;
2904 if((actor->access <= victim->access) && !IsHelping(user))
2906 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2910 /* If people delete themselves it is an override, but they
2911 * could've used deleteme so we don't log it as an override
2913 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2914 override = CMD_LOG_OVERRIDE;
2916 chan_name = strdup(channel->name);
2917 del_channel_user(victim, 1);
2918 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2920 return 1 | override;
2924 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2926 struct userData *actor, *real_actor, *uData, *next;
2927 unsigned int override = 0;
2929 actor = GetChannelUser(channel->channel_info, user->handle_info);
2930 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2932 if(min_access > max_access)
2934 reply("CSMSG_BAD_RANGE", min_access, max_access);
2938 if(actor->access <= max_access)
2940 reply("CSMSG_NO_ACCESS");
2944 if(!real_actor || real_actor->access <= max_access)
2945 override = CMD_LOG_OVERRIDE;
2947 for(uData = channel->channel_info->users; uData; uData = next)
2951 if((uData->access >= min_access)
2952 && (uData->access <= max_access)
2953 && match_ircglob(uData->handle->handle, mask))
2954 del_channel_user(uData, 1);
2957 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2958 return 1 | override;
2961 static CHANSERV_FUNC(cmd_mdelowner)
2963 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2966 static CHANSERV_FUNC(cmd_mdelcoowner)
2968 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2971 static CHANSERV_FUNC(cmd_mdelmaster)
2973 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2976 static CHANSERV_FUNC(cmd_mdelop)
2978 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2981 static CHANSERV_FUNC(cmd_mdelpeon)
2983 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2987 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2989 struct banData *bData, *next;
2990 char interval[INTERVALLEN];
2992 unsigned long limit;
2995 limit = now - duration;
2996 for(bData = channel->channel_info->bans; bData; bData = next)
3000 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3003 del_channel_ban(bData);
3007 intervalString(interval, duration, user->handle_info);
3008 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3013 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3015 struct userData *actor, *uData, *next;
3016 char interval[INTERVALLEN];
3018 unsigned long limit;
3020 actor = GetChannelUser(channel->channel_info, user->handle_info);
3021 if(min_access > max_access)
3023 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3027 if(!actor || actor->access <= max_access)
3029 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3034 limit = now - duration;
3035 for(uData = channel->channel_info->users; uData; uData = next)
3039 if((uData->seen > limit)
3041 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3044 if(((uData->access >= min_access) && (uData->access <= max_access))
3045 || (!max_access && (uData->access < actor->access)))
3047 del_channel_user(uData, 1);
3055 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3057 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3061 static CHANSERV_FUNC(cmd_trim)
3063 unsigned long duration;
3064 unsigned short min_level, max_level;
3069 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3070 duration = ParseInterval(argv[2]);
3073 reply("CSMSG_CANNOT_TRIM");
3077 if(!irccasecmp(argv[1], "bans"))
3079 cmd_trim_bans(user, channel, duration);
3082 else if(!irccasecmp(argv[1], "users"))
3084 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3087 else if(parse_level_range(&min_level, &max_level, argv[1]))
3089 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3092 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3094 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3099 reply("CSMSG_INVALID_TRIM", argv[1]);
3104 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3105 to the user. cmd_all takes advantage of this. */
3106 static CHANSERV_FUNC(cmd_up)
3108 struct mod_chanmode change;
3109 struct userData *uData;
3112 mod_chanmode_init(&change);
3114 change.args[0].u.member = GetUserMode(channel, user);
3115 if(!change.args[0].u.member)
3118 reply("MSG_CHANNEL_ABSENT", channel->name);
3122 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3126 reply("CSMSG_GODMODE_UP", argv[0]);
3129 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3131 change.args[0].mode = MODE_CHANOP;
3132 errmsg = "CSMSG_ALREADY_OPPED";
3134 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3136 change.args[0].mode = MODE_VOICE;
3137 errmsg = "CSMSG_ALREADY_VOICED";
3142 reply("CSMSG_NO_ACCESS");
3145 change.args[0].mode &= ~change.args[0].u.member->modes;
3146 if(!change.args[0].mode)
3149 reply(errmsg, channel->name);
3152 modcmd_chanmode_announce(&change);
3156 static CHANSERV_FUNC(cmd_down)
3158 struct mod_chanmode change;
3160 mod_chanmode_init(&change);
3162 change.args[0].u.member = GetUserMode(channel, user);
3163 if(!change.args[0].u.member)
3166 reply("MSG_CHANNEL_ABSENT", channel->name);
3170 if(!change.args[0].u.member->modes)
3173 reply("CSMSG_ALREADY_DOWN", channel->name);
3177 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3178 modcmd_chanmode_announce(&change);
3182 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)
3184 struct userData *cList;
3186 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3188 if(IsSuspended(cList->channel)
3189 || IsUserSuspended(cList)
3190 || !GetUserMode(cList->channel->channel, user))
3193 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3199 static CHANSERV_FUNC(cmd_upall)
3201 return cmd_all(CSFUNC_ARGS, cmd_up);
3204 static CHANSERV_FUNC(cmd_downall)
3206 return cmd_all(CSFUNC_ARGS, cmd_down);
3209 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3210 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3213 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)
3215 unsigned int ii, valid;
3216 struct userNode *victim;
3217 struct mod_chanmode *change;
3219 change = mod_chanmode_alloc(argc - 1);
3221 for(ii=valid=0; ++ii < argc; )
3223 if(!(victim = GetUserH(argv[ii])))
3225 change->args[valid].mode = mode;
3226 change->args[valid].u.member = GetUserMode(channel, victim);
3227 if(!change->args[valid].u.member)
3229 if(validate && !validate(user, channel, victim))
3234 change->argc = valid;
3235 if(valid < (argc-1))
3236 reply("CSMSG_PROCESS_FAILED");
3239 modcmd_chanmode_announce(change);
3240 reply(action, channel->name);
3242 mod_chanmode_free(change);
3246 static CHANSERV_FUNC(cmd_op)
3248 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3251 static CHANSERV_FUNC(cmd_deop)
3253 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3256 static CHANSERV_FUNC(cmd_voice)
3258 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3261 static CHANSERV_FUNC(cmd_devoice)
3263 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3266 static CHANSERV_FUNC(cmd_opme)
3268 struct mod_chanmode change;
3271 mod_chanmode_init(&change);
3273 change.args[0].u.member = GetUserMode(channel, user);
3274 if(!change.args[0].u.member)
3277 reply("MSG_CHANNEL_ABSENT", channel->name);
3281 struct devnull_class *devnull;
3282 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3284 change.args[0].mode = MODE_CHANOP;
3285 errmsg = "CSMSG_ALREADY_OPPED";
3290 reply("CSMSG_NO_ACCESS");
3293 change.args[0].mode &= ~change.args[0].u.member->modes;
3294 if(!change.args[0].mode)
3297 reply(errmsg, channel->name);
3300 modcmd_chanmode_announce(&change);
3305 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3311 for(ii=0; ii<channel->members.used; ii++)
3313 struct modeNode *mn = channel->members.list[ii];
3315 if(IsService(mn->user))
3318 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3321 if(protect_user(mn->user, user, channel->channel_info))
3325 victims[(*victimCount)++] = mn;
3331 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3333 struct userNode *victim;
3334 struct modeNode **victims;
3335 unsigned int offset, n, victimCount, duration = 0;
3336 char *reason = "Bye.", *ban, *name;
3337 char interval[INTERVALLEN];
3339 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3340 REQUIRE_PARAMS(offset);
3341 if(argc > offset && IsNetServ(user))
3343 if(*argv[offset] == '$') {
3344 struct userNode *hib;
3345 const char *accountnameb = argv[offset] + 1;
3346 if(!(hib = GetUserH(accountnameb)))
3348 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3357 reason = unsplit_string(argv + offset, argc - offset, NULL);
3358 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3360 /* Truncate the reason to a length of TOPICLEN, as
3361 the ircd does; however, leave room for an ellipsis
3362 and the kicker's nick. */
3363 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3367 if((victim = GetUserH(argv[1])))
3369 victims = alloca(sizeof(victims[0]));
3370 victims[0] = GetUserMode(channel, victim);
3371 /* XXX: The comparison with ACTION_KICK is just because all
3372 * other actions can work on users outside the channel, and we
3373 * want to allow those (e.g. unbans) in that case. If we add
3374 * some other ejection action for in-channel users, change
3376 victimCount = victims[0] ? 1 : 0;
3378 if(IsService(victim))
3380 reply("MSG_SERVICE_IMMUNE", victim->nick);
3384 if((action == ACTION_KICK) && !victimCount)
3386 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3390 if(protect_user(victim, user, channel->channel_info))
3392 reply("CSMSG_USER_PROTECTED", victim->nick);
3396 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3397 name = victim->nick;
3399 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3401 struct handle_info *hi;
3402 extern const char *titlehost_suffix;
3403 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3404 const char *accountname = argv[1] + 1;
3406 if(!(hi = get_handle_info(accountname)))
3408 reply("MSG_HANDLE_UNKNOWN", accountname);
3412 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3413 victims = alloca(sizeof(victims[0]) * channel->members.used);
3415 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3417 reply("CSMSG_MASK_PROTECTED", banmask);
3421 if((action == ACTION_KICK) && (victimCount == 0))
3423 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3427 name = ban = strdup(banmask);
3431 if(!is_ircmask(argv[1]))
3433 reply("MSG_NICK_UNKNOWN", argv[1]);
3437 victims = alloca(sizeof(victims[0]) * channel->members.used);
3439 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3441 reply("CSMSG_MASK_PROTECTED", argv[1]);
3445 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3447 reply("CSMSG_LAME_MASK", argv[1]);
3451 if((action == ACTION_KICK) && (victimCount == 0))
3453 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3457 name = ban = strdup(argv[1]);
3460 /* Truncate the ban in place if necessary; we must ensure
3461 that 'ban' is a valid ban mask before sanitizing it. */
3462 sanitize_ircmask(ban);
3464 if(action & ACTION_ADD_BAN)
3466 struct banData *bData, *next;
3468 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3470 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3475 if(action & ACTION_ADD_TIMED_BAN)
3477 duration = ParseInterval(argv[2]);
3481 reply("CSMSG_DURATION_TOO_LOW");
3485 else if(duration > (86400 * 365 * 2))
3487 reply("CSMSG_DURATION_TOO_HIGH");
3493 for(bData = channel->channel_info->bans; bData; bData = next)
3495 if(match_ircglobs(bData->mask, ban))
3497 int exact = !irccasecmp(bData->mask, ban);
3499 /* The ban is redundant; there is already a ban
3500 with the same effect in place. */
3504 free(bData->reason);
3505 bData->reason = strdup(reason);
3506 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3508 reply("CSMSG_REASON_CHANGE", ban);
3512 if(exact && bData->expires)
3516 /* If the ban matches an existing one exactly,
3517 extend the expiration time if the provided
3518 duration is longer. */
3519 if(duration && (now + duration > bData->expires))
3521 bData->expires = now + duration;
3532 /* Delete the expiration timeq entry and
3533 requeue if necessary. */
3534 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3537 timeq_add(bData->expires, expire_ban, bData);
3541 /* automated kickban */
3544 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3546 reply("CSMSG_BAN_ADDED", name, channel->name);
3552 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3559 if(match_ircglobs(ban, bData->mask))
3561 /* The ban we are adding makes previously existing
3562 bans redundant; silently remove them. */
3563 del_channel_ban(bData);
3567 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);
3569 name = ban = strdup(bData->mask);
3573 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3575 extern const char *hidden_host_suffix;
3576 const char *old_name = chanserv_conf.old_ban_names->list[n];
3578 unsigned int l1, l2;
3581 l2 = strlen(old_name);
3584 if(irccasecmp(ban + l1 - l2, old_name))
3586 new_mask = malloc(MAXLEN);
3587 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3589 name = ban = new_mask;
3594 if(action & ACTION_BAN)
3596 unsigned int exists;
3597 struct mod_chanmode *change;
3599 if(channel->banlist.used >= MAXBANS)
3602 reply("CSMSG_BANLIST_FULL", channel->name);
3607 exists = ChannelBanExists(channel, ban);
3608 change = mod_chanmode_alloc(victimCount + 1);
3609 for(n = 0; n < victimCount; ++n)
3611 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3612 change->args[n].u.member = victims[n];
3616 change->args[n].mode = MODE_BAN;
3617 change->args[n++].u.hostmask = ban;
3621 modcmd_chanmode_announce(change);
3623 mod_chanmode_announce(chanserv, channel, change);
3624 mod_chanmode_free(change);
3626 if(exists && (action == ACTION_BAN))
3629 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3635 if(action & ACTION_KICK)
3637 char kick_reason[MAXLEN];
3638 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3640 for(n = 0; n < victimCount; n++)
3641 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3646 /* No response, since it was automated. */
3648 else if(action & ACTION_ADD_BAN)
3651 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3653 reply("CSMSG_BAN_ADDED", name, channel->name);
3655 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3656 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3657 else if(action & ACTION_BAN)
3658 reply("CSMSG_BAN_DONE", name, channel->name);
3659 else if(action & ACTION_KICK && victimCount)
3660 reply("CSMSG_KICK_DONE", name, channel->name);
3666 static CHANSERV_FUNC(cmd_kickban)
3668 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3671 static CHANSERV_FUNC(cmd_kick)
3673 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3676 static CHANSERV_FUNC(cmd_ban)
3678 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3681 static CHANSERV_FUNC(cmd_addban)
3683 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3686 static CHANSERV_FUNC(cmd_addtimedban)
3688 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3691 struct mod_chanmode *
3692 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3694 struct mod_chanmode *change;
3695 unsigned char *match;
3696 unsigned int ii, count;
3698 match = alloca(bans->used);
3701 for(ii = count = 0; ii < bans->used; ++ii)
3703 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3704 MATCH_USENICK | MATCH_VISIBLE);
3711 for(ii = count = 0; ii < bans->used; ++ii)
3713 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3720 change = mod_chanmode_alloc(count);
3721 for(ii = count = 0; ii < bans->used; ++ii)
3725 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3726 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3728 assert(count == change->argc);
3733 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3735 struct userNode *actee;
3741 /* may want to allow a comma delimited list of users... */
3742 if(!(actee = GetUserH(argv[1])))
3744 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3746 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3747 const char *accountname = argv[1] + 1;
3749 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3750 mask = strdup(banmask);
3752 else if(!is_ircmask(argv[1]))
3754 reply("MSG_NICK_UNKNOWN", argv[1]);
3759 mask = strdup(argv[1]);
3763 /* We don't sanitize the mask here because ircu
3765 if(action & ACTION_UNBAN)
3767 struct mod_chanmode *change;
3768 change = find_matching_bans(&channel->banlist, actee, mask);
3773 modcmd_chanmode_announce(change);
3774 for(ii = 0; ii < change->argc; ++ii)
3775 free((char*)change->args[ii].u.hostmask);
3776 mod_chanmode_free(change);
3781 if(action & ACTION_DEL_BAN)
3783 struct banData *ban, *next;
3785 ban = channel->channel_info->bans;
3789 for( ; ban && !user_matches_glob(actee, ban->mask,
3790 MATCH_USENICK | MATCH_VISIBLE);
3793 for( ; ban && !match_ircglobs(mask, ban->mask);
3798 del_channel_ban(ban);
3805 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3807 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3813 static CHANSERV_FUNC(cmd_unban)
3815 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3818 static CHANSERV_FUNC(cmd_delban)
3820 /* it doesn't necessarily have to remove the channel ban - may want
3821 to make that an option. */
3822 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3825 static CHANSERV_FUNC(cmd_unbanme)
3827 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3828 long flags = ACTION_UNBAN;
3830 /* remove permanent bans if the user has the proper access. */
3831 if(uData->access >= UL_MASTER)
3832 flags |= ACTION_DEL_BAN;
3834 argv[1] = user->nick;
3835 return unban_user(user, channel, 2, argv, cmd, flags);
3838 static CHANSERV_FUNC(cmd_unbanall)
3840 struct mod_chanmode *change;
3843 if(!channel->banlist.used)
3845 reply("CSMSG_NO_BANS", channel->name);
3849 change = mod_chanmode_alloc(channel->banlist.used);
3850 for(ii=0; ii<channel->banlist.used; ii++)
3852 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3853 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3855 modcmd_chanmode_announce(change);
3856 for(ii = 0; ii < change->argc; ++ii)
3857 free((char*)change->args[ii].u.hostmask);
3858 mod_chanmode_free(change);
3859 reply("CSMSG_BANS_REMOVED", channel->name);
3863 static CHANSERV_FUNC(cmd_open)
3865 struct mod_chanmode *change;
3868 change = find_matching_bans(&channel->banlist, user, NULL);
3870 change = mod_chanmode_alloc(0);
3871 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3872 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3873 && channel->channel_info->modes.modes_set)
3874 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3875 modcmd_chanmode_announce(change);
3876 reply("CSMSG_CHANNEL_OPENED", channel->name);
3877 for(ii = 0; ii < change->argc; ++ii)
3878 free((char*)change->args[ii].u.hostmask);
3879 mod_chanmode_free(change);
3883 static CHANSERV_FUNC(cmd_myaccess)
3885 static struct string_buffer sbuf;
3886 struct handle_info *target_handle;
3887 struct userData *uData;
3892 target_handle = user->handle_info;
3893 else if(!IsStaff(user))
3895 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3898 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3901 if(!oper_outranks(user, target_handle))
3904 if(!target_handle->channels)
3906 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3910 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3911 for(uData = target_handle->channels; uData; uData = uData->u_next)
3913 struct chanData *cData = uData->channel;
3915 unsigned int base_len;
3917 if(uData->access > UL_OWNER)
3919 if(uData->access == UL_OWNER)
3922 if(IsProtected(cData)
3923 && (target_handle != user->handle_info)
3924 && !GetTrueChannelAccess(cData, user->handle_info)
3925 && !IsNetworkHelper(user))
3928 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3929 base_len = sbuf.used;
3930 if(IsUserSuspended(uData))
3931 string_buffer_append(&sbuf, 's');
3932 if(IsUserAutoOp(uData))
3934 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3935 string_buffer_append(&sbuf, 'o');
3936 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3937 string_buffer_append(&sbuf, 'v');
3939 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3940 string_buffer_append(&sbuf, 'i');
3941 if(sbuf.used==base_len)
3944 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3946 string_buffer_append_string(&sbuf, ")]");
3947 string_buffer_append(&sbuf, '\0');
3948 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3952 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3954 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3960 static CHANSERV_FUNC(cmd_access)
3962 struct userNode *target;
3963 struct handle_info *target_handle;
3964 struct userData *uData;
3966 char prefix[MAXLEN];
3971 target_handle = target->handle_info;
3973 else if((target = GetUserH(argv[1])))
3975 target_handle = target->handle_info;
3977 else if(argv[1][0] == '*')
3979 if(!(target_handle = get_handle_info(argv[1]+1)))
3981 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3987 reply("MSG_NICK_UNKNOWN", argv[1]);
3991 assert(target || target_handle);
3993 if(target == chanserv)
3995 reply("CSMSG_IS_CHANSERV");
4003 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4008 reply("MSG_USER_AUTHENTICATE", target->nick);
4011 reply("MSG_AUTHENTICATE");
4017 const char *epithet = NULL, *type = NULL;
4020 epithet = chanserv_conf.irc_operator_epithet;
4021 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4023 else if(IsNetworkHelper(target))
4025 epithet = chanserv_conf.network_helper_epithet;
4026 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4028 else if(IsSupportHelper(target))
4030 epithet = chanserv_conf.support_helper_epithet;
4031 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4035 if(target_handle->epithet)
4036 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4038 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4040 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4044 sprintf(prefix, "%s", target_handle->handle);
4047 if(!channel->channel_info)
4049 reply("CSMSG_NOT_REGISTERED", channel->name);
4053 helping = HANDLE_FLAGGED(target_handle, HELPING)
4054 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4055 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4057 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4058 /* To prevent possible information leaks, only show infolines
4059 * if the requestor is in the channel or it's their own
4061 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4063 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4065 /* Likewise, only say it's suspended if the user has active
4066 * access in that channel or it's their own entry. */
4067 if(IsUserSuspended(uData)
4068 && (GetChannelUser(channel->channel_info, user->handle_info)
4069 || (user->handle_info == uData->handle)))
4071 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4076 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4083 def_list(struct listData *list)
4087 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4089 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4090 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4091 if(list->table.length == 1)
4093 msg = user_find_message(list->user, "MSG_NONE");
4094 send_message_type(4, list->user, list->bot, " %s", msg);
4099 userData_access_comp(const void *arg_a, const void *arg_b)
4101 const struct userData *a = *(struct userData**)arg_a;
4102 const struct userData *b = *(struct userData**)arg_b;
4104 if(a->access != b->access)
4105 res = b->access - a->access;
4107 res = irccasecmp(a->handle->handle, b->handle->handle);
4112 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4114 void (*send_list)(struct listData *);
4115 struct userData *uData;
4116 struct listData lData;
4117 unsigned int matches;
4121 lData.bot = cmd->parent->bot;
4122 lData.channel = channel;
4123 lData.lowest = lowest;
4124 lData.highest = highest;
4125 lData.search = (argc > 1) ? argv[1] : NULL;
4126 send_list = def_list;
4128 if(user->handle_info)
4130 switch(user->handle_info->userlist_style)
4132 case HI_STYLE_DEF: send_list = def_list; break;
4133 case HI_STYLE_ZOOT: send_list = def_list; break;
4137 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4139 for(uData = channel->channel_info->users; uData; uData = uData->next)
4141 if((uData->access < lowest)
4142 || (uData->access > highest)
4143 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4145 lData.users[matches++] = uData;
4147 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4149 lData.table.length = matches+1;
4150 lData.table.width = 4;
4151 lData.table.flags = TABLE_NO_FREE;
4152 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4153 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4154 lData.table.contents[0] = ary;
4157 ary[2] = "Last Seen";
4159 for(matches = 1; matches < lData.table.length; ++matches)
4161 char seen[INTERVALLEN];
4163 uData = lData.users[matches-1];
4164 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4165 lData.table.contents[matches] = ary;
4166 ary[0] = strtab(uData->access);
4167 ary[1] = uData->handle->handle;
4170 else if(!uData->seen)
4173 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4174 ary[2] = strdup(ary[2]);
4175 if(IsUserSuspended(uData))
4176 ary[3] = "Suspended";
4177 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4178 ary[3] = "Vacation";
4179 else if(HANDLE_FLAGGED(uData->handle, BOT))
4185 for(matches = 1; matches < lData.table.length; ++matches)
4187 free((char*)lData.table.contents[matches][2]);
4188 free(lData.table.contents[matches]);
4190 free(lData.table.contents[0]);
4191 free(lData.table.contents);
4192 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4196 static CHANSERV_FUNC(cmd_users)
4198 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4201 static CHANSERV_FUNC(cmd_wlist)
4203 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4206 static CHANSERV_FUNC(cmd_clist)
4208 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4211 static CHANSERV_FUNC(cmd_mlist)
4213 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4216 static CHANSERV_FUNC(cmd_olist)
4218 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4221 static CHANSERV_FUNC(cmd_plist)
4223 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4226 static CHANSERV_FUNC(cmd_bans)
4228 struct userNode *search_u = NULL;
4229 struct helpfile_table tbl;
4230 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4231 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4232 const char *msg_never, *triggered, *expires;
4233 struct banData *ban, **bans;
4237 else if(strchr(search = argv[1], '!'))
4240 search_wilds = search[strcspn(search, "?*")];
4242 else if(!(search_u = GetUserH(search)))
4243 reply("MSG_NICK_UNKNOWN", search);
4245 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4247 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4251 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4256 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4259 bans[matches++] = ban;
4264 tbl.length = matches + 1;
4265 tbl.width = 4 + timed;
4267 tbl.flags = TABLE_NO_FREE;
4268 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4269 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4270 tbl.contents[0][0] = "Mask";
4271 tbl.contents[0][1] = "Set By";
4272 tbl.contents[0][2] = "Triggered";
4275 tbl.contents[0][3] = "Expires";
4276 tbl.contents[0][4] = "Reason";
4279 tbl.contents[0][3] = "Reason";
4282 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4284 free(tbl.contents[0]);
4289 msg_never = user_find_message(user, "MSG_NEVER");
4290 for(ii = 0; ii < matches; )
4296 else if(ban->expires)
4297 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4299 expires = msg_never;
4302 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4304 triggered = msg_never;
4306 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4307 tbl.contents[ii][0] = ban->mask;
4308 tbl.contents[ii][1] = ban->owner;
4309 tbl.contents[ii][2] = strdup(triggered);
4312 tbl.contents[ii][3] = strdup(expires);
4313 tbl.contents[ii][4] = ban->reason;
4316 tbl.contents[ii][3] = ban->reason;
4318 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4319 reply("MSG_MATCH_COUNT", matches);
4320 for(ii = 1; ii < tbl.length; ++ii)
4322 free((char*)tbl.contents[ii][2]);
4324 free((char*)tbl.contents[ii][3]);
4325 free(tbl.contents[ii]);
4327 free(tbl.contents[0]);
4333 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4335 struct chanData *cData = channel->channel_info;
4336 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4338 if(cData->topic_mask)
4340 if(cData->flags & CHANNEL_ADVTOPIC)
4342 //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4343 char topicmask[TOPICLEN];
4344 int skipnum, topicpos = 0;
4345 char *ptr = cData->topic_mask;
4346 for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4349 for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4351 topicmask[topicpos++] = '*';
4353 topicmask[topicpos++] = *ptr;
4356 topicmask[topicpos++] = *ptr;
4360 topicmask[topicpos] = 0;
4361 return !match_ircglob(new_topic, topicmask);
4364 return !match_ircglob(new_topic, cData->topic_mask);
4366 else if(cData->topic)
4367 return irccasecmp(new_topic, cData->topic);
4372 static CHANSERV_FUNC(cmd_topic)
4374 struct chanData *cData;
4377 cData = channel->channel_info;
4382 SetChannelTopic(channel, chanserv, cData->topic, 1);
4383 reply("CSMSG_TOPIC_SET", cData->topic);
4387 reply("CSMSG_NO_TOPIC", channel->name);
4391 topic = unsplit_string(argv + 1, argc - 1, NULL);
4392 /* If they say "!topic *", use an empty topic. */
4393 if((topic[0] == '*') && (topic[1] == 0))
4395 if(bad_topic(channel, user, topic))
4397 char *topic_mask = cData->topic_mask;
4400 char new_topic[TOPICLEN+1], tchar;
4401 int pos=0, starpos=-1, dpos=0, len;
4403 if(cData->flags & CHANNEL_ADVTOPIC)
4405 //first check if there is a leading 'modifier id'
4406 int advtopic_index = 0;
4409 for(; topic[pos]; pos++)
4411 if(topic[pos] == ' ')
4413 //leading number found, cut off and store value in advtopic_index
4415 advtopic_index = atoi(topic) - 1; //no zerobase
4416 topic = &topic[pos+1];
4417 /* If they say "!topic 2 *", unset advtopic id 2. */
4418 if((topic[0] == '*') && (topic[1] == 0))
4421 if(!isdigit(topic[pos]))
4424 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4427 reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4430 if(cData->advtopic[advtopic_index])
4431 free(cData->advtopic[advtopic_index]);
4432 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4433 char *ptr = topic_mask;
4434 while(*ptr && (dpos <= TOPICLEN))
4440 for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4441 numbuf[numpos++] = *ptr;
4444 if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4446 new_topic[dpos++] = *ptr; //is % again
4449 advtopic_index--; //no zero base
4450 if(!cData->advtopic[advtopic_index])
4451 break; //just leave it empty
4452 len = strlen(cData->advtopic[advtopic_index]);
4453 if((dpos + len) > TOPICLEN)
4454 len = TOPICLEN + 1 - dpos;
4455 memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4459 ptr++; /* and fall through */
4462 new_topic[dpos++] = *ptr;
4468 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4475 len = strlen(topic);
4476 if((dpos + len) > TOPICLEN)
4477 len = TOPICLEN + 1 - dpos;
4478 memcpy(new_topic+dpos, topic, len);
4482 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4483 default: new_topic[dpos++] = tchar; break;
4486 if((dpos > TOPICLEN) || tchar)
4489 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4490 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4494 new_topic[dpos] = 0;
4495 SetChannelTopic(channel, chanserv, new_topic, 1);
4497 reply("CSMSG_TOPIC_LOCKED", channel->name);
4502 SetChannelTopic(channel, chanserv, topic, 1);
4504 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4506 /* Grab the topic and save it as the default topic. */
4508 cData->topic = strdup(channel->topic);
4514 static CHANSERV_FUNC(cmd_mode)
4516 struct userData *uData;
4517 struct mod_chanmode *change;
4523 change = &channel->channel_info->modes;
4524 if(change->modes_set || change->modes_clear) {
4525 modcmd_chanmode_announce(change);
4526 reply("CSMSG_DEFAULTED_MODES", channel->name);
4528 reply("CSMSG_NO_MODES", channel->name);
4532 uData = GetChannelUser(channel->channel_info, user->handle_info);
4534 base_oplevel = MAXOPLEVEL;
4535 else if (uData->access >= UL_OWNER)
4538 base_oplevel = 1 + UL_OWNER - uData->access;
4539 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4542 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4546 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4547 && mode_lock_violated(&channel->channel_info->modes, change))
4550 mod_chanmode_format(&channel->channel_info->modes, modes);
4551 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4555 modcmd_chanmode_announce(change);
4556 mod_chanmode_format(change, fmt);
4557 mod_chanmode_free(change);
4558 reply("CSMSG_MODES_SET", fmt);
4563 chanserv_del_invite_mark(void *data)
4565 struct ChanUser *chanuser = data;
4566 struct chanNode *channel = chanuser->chan;
4568 if(!channel) return;
4569 for(i = 0; i < channel->invited.used; i++)
4571 if(channel->invited.list[i] == chanuser->user) {
4572 userList_remove(&channel->invited, chanuser->user);
4578 static CHANSERV_FUNC(cmd_invite)
4580 struct userNode *invite;
4581 struct ChanUser *chanuser;
4586 if(!(invite = GetUserH(argv[1])))
4588 reply("MSG_NICK_UNKNOWN", argv[1]);
4595 if(GetUserMode(channel, invite))
4597 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4601 for(i = 0; i < channel->invited.used; i++)
4603 if(channel->invited.list[i] == invite) {
4604 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4613 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4614 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4617 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4619 irc_invite(chanserv, invite, channel);
4621 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4623 userList_append(&channel->invited, invite);
4624 chanuser = calloc(1, sizeof(*chanuser));
4625 chanuser->user=invite;
4626 chanuser->chan=channel;
4627 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4632 static CHANSERV_FUNC(cmd_inviteme)
4634 if(GetUserMode(channel, user))
4636 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4639 if(channel->channel_info
4640 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4642 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4645 irc_invite(cmd->parent->bot, user, channel);
4649 static CHANSERV_FUNC(cmd_invitemeall)
4651 struct handle_info *target = user->handle_info;
4652 struct userData *uData;
4654 if(!target->channels)
4656 reply("CSMSG_SQUAT_ACCESS", target->handle);
4660 for(uData = target->channels; uData; uData = uData->u_next)
4662 struct chanData *cData = uData->channel;
4663 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4665 irc_invite(cmd->parent->bot, user, cData->channel);
4672 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4675 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4677 /* We display things based on two dimensions:
4678 * - Issue time: present or absent
4679 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4680 * (in order of precedence, so something both expired and revoked
4681 * only counts as revoked)
4683 combo = (suspended->issued ? 4 : 0)
4684 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4686 case 0: /* no issue time, indefinite expiration */
4687 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4689 case 1: /* no issue time, expires in future */
4690 intervalString(buf1, suspended->expires-now, user->handle_info);
4691 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4693 case 2: /* no issue time, expired */
4694 intervalString(buf1, now-suspended->expires, user->handle_info);
4695 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4697 case 3: /* no issue time, revoked */
4698 intervalString(buf1, now-suspended->revoked, user->handle_info);
4699 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4701 case 4: /* issue time set, indefinite expiration */
4702 intervalString(buf1, now-suspended->issued, user->handle_info);
4703 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4705 case 5: /* issue time set, expires in future */
4706 intervalString(buf1, now-suspended->issued, user->handle_info);
4707 intervalString(buf2, suspended->expires-now, user->handle_info);
4708 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4710 case 6: /* issue time set, expired */
4711 intervalString(buf1, now-suspended->issued, user->handle_info);
4712 intervalString(buf2, now-suspended->expires, user->handle_info);
4713 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4715 case 7: /* issue time set, revoked */
4716 intervalString(buf1, now-suspended->issued, user->handle_info);
4717 intervalString(buf2, now-suspended->revoked, user->handle_info);
4718 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4721 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4727 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4730 const char *fmt = "%a %b %d %H:%M %Y";
4731 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4733 if(giveownership->staff_issuer)
4735 if(giveownership->reason)
4736 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4737 giveownership->target, giveownership->target_access,
4738 giveownership->staff_issuer, buf, giveownership->reason);
4740 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4741 giveownership->target, giveownership->target_access,
4742 giveownership->staff_issuer, buf);
4746 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4750 static CHANSERV_FUNC(cmd_info)
4752 char modes[MAXLEN], buffer[INTERVALLEN];
4753 struct userData *uData, *owner;
4754 struct chanData *cData;
4755 struct do_not_register *dnr;
4760 cData = channel->channel_info;
4761 reply("CSMSG_CHANNEL_INFO", channel->name);
4763 uData = GetChannelUser(cData, user->handle_info);
4764 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4766 mod_chanmode_format(&cData->modes, modes);
4767 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4768 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4771 for(it = dict_first(cData->notes); it; it = iter_next(it))
4775 note = iter_data(it);
4776 if(!note_type_visible_to_user(cData, note->type, user))
4779 padding = PADLEN - 1 - strlen(iter_key(it));
4780 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4783 if(cData->max_time) {
4784 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4786 reply("CSMSG_CHANNEL_MAX", cData->max);
4788 for(owner = cData->users; owner; owner = owner->next)
4789 if(owner->access == UL_OWNER)
4790 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4791 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4792 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4793 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4795 privileged = IsStaff(user);
4797 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4798 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4799 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4801 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4802 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4804 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4806 struct suspended *suspended;
4807 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4808 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4809 show_suspension_info(cmd, user, suspended);
4811 else if(IsSuspended(cData))
4813 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4814 show_suspension_info(cmd, user, cData->suspended);
4817 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4819 struct giveownership *giveownership;
4820 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4821 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4822 show_giveownership_info(cmd, user, giveownership);
4827 static CHANSERV_FUNC(cmd_netinfo)
4829 extern unsigned long boot_time;
4830 extern unsigned long burst_length;
4831 char interval[INTERVALLEN];
4833 reply("CSMSG_NETWORK_INFO");
4834 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4835 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4836 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4837 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4838 reply("CSMSG_NETWORK_BANS", banCount);
4839 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4840 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4841 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4846 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4848 struct helpfile_table table;
4850 struct userNode *user;
4855 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4856 table.contents = alloca(list->used*sizeof(*table.contents));
4857 for(nn=0; nn<list->used; nn++)
4859 user = list->list[nn];
4860 if(user->modes & skip_flags)
4866 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4869 nick = alloca(strlen(user->nick)+3);
4870 sprintf(nick, "(%s)", user->nick);
4874 table.contents[table.length][0] = nick;
4877 table_send(chanserv, to->nick, 0, NULL, table);
4880 static CHANSERV_FUNC(cmd_ircops)
4882 reply("CSMSG_STAFF_OPERS");
4883 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4887 static CHANSERV_FUNC(cmd_helpers)
4889 reply("CSMSG_STAFF_HELPERS");
4890 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4894 static CHANSERV_FUNC(cmd_staff)
4896 reply("CSMSG_NETWORK_STAFF");
4897 cmd_ircops(CSFUNC_ARGS);
4898 cmd_helpers(CSFUNC_ARGS);
4902 static CHANSERV_FUNC(cmd_peek)
4904 struct modeNode *mn;
4905 char modes[MODELEN];
4907 struct helpfile_table table;
4908 int opcount = 0, voicecount = 0, srvcount = 0;
4910 irc_make_chanmode(channel, modes);
4912 reply("CSMSG_PEEK_INFO", channel->name);
4913 reply("CSMSG_PEEK_TOPIC", channel->topic);
4914 reply("CSMSG_PEEK_MODES", modes);
4918 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4919 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4920 for(n = 0; n < channel->members.used; n++)
4922 mn = channel->members.list[n];
4923 if(IsLocal(mn->user))
4925 else if(mn->modes & MODE_CHANOP)
4927 else if(mn->modes & MODE_VOICE)
4930 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4932 table.contents[table.length] = alloca(sizeof(**table.contents));
4933 table.contents[table.length][0] = mn->user->nick;
4937 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4938 (channel->members.used - opcount - voicecount - srvcount));
4942 reply("CSMSG_PEEK_OPS");
4943 table_send(chanserv, user->nick, 0, NULL, table);
4946 reply("CSMSG_PEEK_NO_OPS");
4950 static MODCMD_FUNC(cmd_wipeinfo)
4952 struct handle_info *victim;
4953 struct userData *ud, *actor, *real_actor;
4954 unsigned int override = 0;
4957 actor = GetChannelUser(channel->channel_info, user->handle_info);
4958 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4959 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4961 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4963 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4966 if((ud->access >= actor->access) && (ud != actor))
4968 reply("MSG_USER_OUTRANKED", victim->handle);
4971 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4972 override = CMD_LOG_OVERRIDE;
4976 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4977 return 1 | override;
4980 static CHANSERV_FUNC(cmd_resync)
4982 struct mod_chanmode *changes;
4983 struct chanData *cData = channel->channel_info;
4984 unsigned int ii, used;
4986 changes = mod_chanmode_alloc(channel->members.used * 2);
4987 for(ii = used = 0; ii < channel->members.used; ++ii)
4989 struct modeNode *mn = channel->members.list[ii];
4990 struct userData *uData;
4992 if(IsService(mn->user))
4995 uData = GetChannelAccess(cData, mn->user->handle_info);
4996 if(!cData->lvlOpts[lvlGiveOps]
4997 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4999 if(!(mn->modes & MODE_CHANOP))
5001 if(!uData || IsUserAutoOp(uData))
5003 changes->args[used].mode = MODE_CHANOP;
5004 changes->args[used++].u.member = mn;
5005 if(!(mn->modes & MODE_VOICE))
5007 changes->args[used].mode = MODE_VOICE;
5008 changes->args[used++].u.member = mn;
5013 else if(!cData->lvlOpts[lvlGiveVoice]
5014 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5016 if(mn->modes & MODE_CHANOP)
5018 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5019 changes->args[used++].u.member = mn;
5021 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5023 changes->args[used].mode = MODE_VOICE;
5024 changes->args[used++].u.member = mn;
5031 changes->args[used].mode = MODE_REMOVE | mn->modes;
5032 changes->args[used++].u.member = mn;
5036 changes->argc = used;
5037 modcmd_chanmode_announce(changes);
5038 mod_chanmode_free(changes);
5039 reply("CSMSG_RESYNCED_USERS", channel->name);
5043 static CHANSERV_FUNC(cmd_seen)
5045 struct userData *uData;
5046 struct handle_info *handle;
5047 char seen[INTERVALLEN];
5051 if(!irccasecmp(argv[1], chanserv->nick))
5053 reply("CSMSG_IS_CHANSERV");
5057 if(!(handle = get_handle_info(argv[1])))
5059 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5063 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5065 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5070 reply("CSMSG_USER_PRESENT", handle->handle);
5071 else if(uData->seen)
5072 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5074 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5076 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5077 reply("CSMSG_USER_VACATION", handle->handle);
5082 static MODCMD_FUNC(cmd_names)
5084 struct userNode *targ;
5085 struct userData *targData;
5086 unsigned int ii, pos;
5089 for(ii=pos=0; ii<channel->members.used; ++ii)
5091 targ = channel->members.list[ii]->user;
5092 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5095 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5098 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5102 if(IsUserSuspended(targData))
5104 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5107 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5108 reply("CSMSG_END_NAMES", channel->name);
5113 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5115 switch(ntype->visible_type)
5117 case NOTE_VIS_ALL: return 1;
5118 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5119 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5124 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5126 struct userData *uData;
5128 switch(ntype->set_access_type)
5130 case NOTE_SET_CHANNEL_ACCESS:
5131 if(!user->handle_info)
5133 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5135 return uData->access >= ntype->set_access.min_ulevel;
5136 case NOTE_SET_CHANNEL_SETTER:
5137 return check_user_level(channel, user, lvlSetters, 1, 0);
5138 case NOTE_SET_PRIVILEGED: default:
5139 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5143 static CHANSERV_FUNC(cmd_note)
5145 struct chanData *cData;
5147 struct note_type *ntype;
5149 cData = channel->channel_info;
5152 reply("CSMSG_NOT_REGISTERED", channel->name);
5156 /* If no arguments, show all visible notes for the channel. */
5162 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5164 note = iter_data(it);
5165 if(!note_type_visible_to_user(cData, note->type, user))
5168 reply("CSMSG_NOTELIST_HEADER", channel->name);
5169 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5172 reply("CSMSG_NOTELIST_END", channel->name);
5174 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5176 /* If one argument, show the named note. */
5179 if((note = dict_find(cData->notes, argv[1], NULL))
5180 && note_type_visible_to_user(cData, note->type, user))
5182 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5184 else if((ntype = dict_find(note_types, argv[1], NULL))
5185 && note_type_visible_to_user(NULL, ntype, user))
5187 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5192 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5196 /* Assume they're trying to set a note. */
5200 ntype = dict_find(note_types, argv[1], NULL);
5203 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5206 else if(note_type_settable_by_user(channel, ntype, user))
5208 note_text = unsplit_string(argv+2, argc-2, NULL);
5209 if((note = dict_find(cData->notes, argv[1], NULL)))
5210 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5211 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5212 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5214 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5216 /* The note is viewable to staff only, so return 0
5217 to keep the invocation from getting logged (or
5218 regular users can see it in !events). */
5224 reply("CSMSG_NO_ACCESS");
5231 static CHANSERV_FUNC(cmd_delnote)
5236 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5237 || !note_type_settable_by_user(channel, note->type, user))
5239 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5242 dict_remove(channel->channel_info->notes, note->type->name);
5243 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5247 static CHANSERV_FUNC(cmd_events)
5249 struct logSearch discrim;
5250 struct logReport report;
5251 unsigned int matches, limit;
5253 limit = (argc > 1) ? atoi(argv[1]) : 10;
5254 if(limit < 1 || limit > 200)
5257 memset(&discrim, 0, sizeof(discrim));
5258 discrim.masks.bot = chanserv;
5259 discrim.masks.channel_name = channel->name;
5261 discrim.masks.command = argv[2];
5262 discrim.limit = limit;
5263 discrim.max_time = INT_MAX;
5264 discrim.severities = 1 << LOG_COMMAND;
5265 report.reporter = chanserv;
5267 reply("CSMSG_EVENT_SEARCH_RESULTS");
5268 matches = log_entry_search(&discrim, log_report_entry, &report);
5270 reply("MSG_MATCH_COUNT", matches);
5272 reply("MSG_NO_MATCHES");
5276 static CHANSERV_FUNC(cmd_say)
5282 msg = unsplit_string(argv + 1, argc - 1, NULL);
5283 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5285 else if(*argv[1] == '*' && argv[1][1] != '\0')
5287 struct handle_info *hi;
5288 struct userNode *authed;
5291 msg = unsplit_string(argv + 2, argc - 2, NULL);
5293 if (!(hi = get_handle_info(argv[1] + 1)))
5295 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5299 for (authed = hi->users; authed; authed = authed->next_authed)
5300 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5302 else if(GetUserH(argv[1]))
5305 msg = unsplit_string(argv + 2, argc - 2, NULL);
5306 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5310 reply("MSG_NOT_TARGET_NAME");
5316 static CHANSERV_FUNC(cmd_emote)
5322 /* CTCP is so annoying. */
5323 msg = unsplit_string(argv + 1, argc - 1, NULL);
5324 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5326 else if(*argv[1] == '*' && argv[1][1] != '\0')
5328 struct handle_info *hi;
5329 struct userNode *authed;
5332 msg = unsplit_string(argv + 2, argc - 2, NULL);
5334 if (!(hi = get_handle_info(argv[1] + 1)))
5336 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5340 for (authed = hi->users; authed; authed = authed->next_authed)
5341 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5343 else if(GetUserH(argv[1]))
5345 msg = unsplit_string(argv + 2, argc - 2, NULL);
5346 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5350 reply("MSG_NOT_TARGET_NAME");
5356 struct channelList *
5357 chanserv_support_channels(void)
5359 return &chanserv_conf.support_channels;
5362 static CHANSERV_FUNC(cmd_expire)
5364 int channel_count = registered_channels;
5365 expire_channels(chanserv);
5366 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5371 chanserv_expire_suspension(void *data)
5373 struct suspended *suspended = data;
5374 struct chanNode *channel;
5377 /* Update the channel registration data structure. */
5378 if(!suspended->expires || (now < suspended->expires))
5379 suspended->revoked = now;
5380 channel = suspended->cData->channel;
5381 suspended->cData->channel = channel;
5382 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5384 /* If appropriate, re-join ChanServ to the channel. */
5385 if(!IsOffChannel(suspended->cData))
5387 spamserv_cs_suspend(channel, 0, 0, NULL);
5388 ss_cs_join_channel(channel, 1);
5391 /* Mark everyone currently in the channel as present. */
5392 for(ii = 0; ii < channel->members.used; ++ii)
5394 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5403 static CHANSERV_FUNC(cmd_csuspend)
5405 struct suspended *suspended;
5406 char reason[MAXLEN];
5407 unsigned long expiry, duration;
5408 struct userData *uData;
5412 if(IsProtected(channel->channel_info))
5414 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5418 if(argv[1][0] == '!')
5420 else if(IsSuspended(channel->channel_info))
5422 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5423 show_suspension_info(cmd, user, channel->channel_info->suspended);
5427 if(!strcmp(argv[1], "0"))
5429 else if((duration = ParseInterval(argv[1])))
5430 expiry = now + duration;
5433 reply("MSG_INVALID_DURATION", argv[1]);
5437 unsplit_string(argv + 2, argc - 2, reason);
5439 suspended = calloc(1, sizeof(*suspended));
5440 suspended->revoked = 0;
5441 suspended->issued = now;
5442 suspended->suspender = strdup(user->handle_info->handle);
5443 suspended->expires = expiry;
5444 suspended->reason = strdup(reason);
5445 suspended->cData = channel->channel_info;
5446 suspended->previous = suspended->cData->suspended;
5447 suspended->cData->suspended = suspended;
5449 if(suspended->expires)
5450 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5452 if(IsSuspended(channel->channel_info))
5454 suspended->previous->revoked = now;
5455 if(suspended->previous->expires)
5456 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5457 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5458 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5462 /* Mark all users in channel as absent. */
5463 for(uData = channel->channel_info->users; uData; uData = uData->next)
5472 /* Mark the channel as suspended, then part. */
5473 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5474 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5475 DelChannelUser(chanserv, channel, suspended->reason, 0);
5476 reply("CSMSG_SUSPENDED", channel->name);
5477 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5478 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5483 static CHANSERV_FUNC(cmd_cunsuspend)
5485 struct suspended *suspended;
5486 char message[MAXLEN];
5488 if(!IsSuspended(channel->channel_info))
5490 reply("CSMSG_NOT_SUSPENDED", channel->name);
5494 suspended = channel->channel_info->suspended;
5496 /* Expire the suspension and join ChanServ to the channel. */
5497 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5498 chanserv_expire_suspension(suspended);
5499 reply("CSMSG_UNSUSPENDED", channel->name);
5500 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5501 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5505 typedef struct chanservSearch
5510 unsigned long unvisited;
5511 unsigned long registered;
5513 unsigned long flags;
5517 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5520 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5525 search = malloc(sizeof(struct chanservSearch));
5526 memset(search, 0, sizeof(*search));
5529 for(i = 0; i < argc; i++)
5531 /* Assume all criteria require arguments. */
5534 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5538 if(!irccasecmp(argv[i], "name"))
5539 search->name = argv[++i];
5540 else if(!irccasecmp(argv[i], "registrar"))
5541 search->registrar = argv[++i];
5542 else if(!irccasecmp(argv[i], "unvisited"))
5543 search->unvisited = ParseInterval(argv[++i]);
5544 else if(!irccasecmp(argv[i], "registered"))
5545 search->registered = ParseInterval(argv[++i]);
5546 else if(!irccasecmp(argv[i], "flags"))
5549 if(!irccasecmp(argv[i], "nodelete"))
5550 search->flags |= CHANNEL_NODELETE;
5551 else if(!irccasecmp(argv[i], "suspended"))
5552 search->flags |= CHANNEL_SUSPENDED;
5553 else if(!irccasecmp(argv[i], "unreviewed"))
5554 search->flags |= CHANNEL_UNREVIEWED;
5557 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5561 else if(!irccasecmp(argv[i], "limit"))
5562 search->limit = strtoul(argv[++i], NULL, 10);
5565 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5570 if(search->name && !strcmp(search->name, "*"))
5572 if(search->registrar && !strcmp(search->registrar, "*"))
5573 search->registrar = 0;
5582 chanserv_channel_match(struct chanData *channel, search_t search)
5584 const char *name = channel->channel->name;
5585 if((search->name && !match_ircglob(name, search->name)) ||
5586 (search->registrar && !channel->registrar) ||
5587 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5588 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5589 (search->registered && (now - channel->registered) > search->registered) ||
5590 (search->flags && ((search->flags & channel->flags) != search->flags)))
5597 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5599 struct chanData *channel;
5600 unsigned int matches = 0;
5602 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5604 if(!chanserv_channel_match(channel, search))
5614 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5619 search_print(struct chanData *channel, void *data)
5621 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5624 static CHANSERV_FUNC(cmd_search)
5627 unsigned int matches;
5628 channel_search_func action;
5632 if(!irccasecmp(argv[1], "count"))
5633 action = search_count;
5634 else if(!irccasecmp(argv[1], "print"))
5635 action = search_print;
5638 reply("CSMSG_ACTION_INVALID", argv[1]);
5642 search = chanserv_search_create(user, argc - 2, argv + 2);
5646 if(action == search_count)
5647 search->limit = INT_MAX;
5649 if(action == search_print)
5650 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5652 matches = chanserv_channel_search(search, action, user);
5655 reply("MSG_MATCH_COUNT", matches);
5657 reply("MSG_NO_MATCHES");
5663 static CHANSERV_FUNC(cmd_unvisited)
5665 struct chanData *cData;
5666 unsigned long interval = chanserv_conf.channel_expire_delay;
5667 char buffer[INTERVALLEN];
5668 unsigned int limit = 25, matches = 0;
5672 interval = ParseInterval(argv[1]);
5674 limit = atoi(argv[2]);
5677 intervalString(buffer, interval, user->handle_info);
5678 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5680 for(cData = channelList; cData && matches < limit; cData = cData->next)
5682 if((now - cData->visited) < interval)
5685 intervalString(buffer, now - cData->visited, user->handle_info);
5686 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5693 static MODCMD_FUNC(chan_opt_defaulttopic)
5699 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5701 reply("CSMSG_TOPIC_LOCKED", channel->name);
5705 topic = unsplit_string(argv+1, argc-1, NULL);
5707 free(channel->channel_info->topic);
5708 if(topic[0] == '*' && topic[1] == 0)
5710 topic = channel->channel_info->topic = NULL;
5714 topic = channel->channel_info->topic = strdup(topic);
5715 if(channel->channel_info->topic_mask
5716 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5717 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5719 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5722 if(channel->channel_info->topic)
5723 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5725 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5729 static MODCMD_FUNC(chan_opt_topicmask)
5733 struct chanData *cData = channel->channel_info;
5736 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5738 reply("CSMSG_TOPIC_LOCKED", channel->name);
5742 mask = unsplit_string(argv+1, argc-1, NULL);
5744 if(cData->topic_mask)
5745 free(cData->topic_mask);
5746 if(mask[0] == '*' && mask[1] == 0)
5748 cData->topic_mask = 0;
5752 cData->topic_mask = strdup(mask);
5754 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5755 else if(!match_ircglob(cData->topic, cData->topic_mask))
5756 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5760 if(channel->channel_info->topic_mask)
5761 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5763 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5767 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5771 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5775 if(greeting[0] == '*' && greeting[1] == 0)
5779 unsigned int length = strlen(greeting);
5780 if(length > chanserv_conf.greeting_length)
5782 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5785 *data = strdup(greeting);
5794 reply(name, user_find_message(user, "MSG_NONE"));
5798 static MODCMD_FUNC(chan_opt_greeting)
5800 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5803 static MODCMD_FUNC(chan_opt_usergreeting)
5805 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5808 static MODCMD_FUNC(chan_opt_modes)
5810 struct mod_chanmode *new_modes;
5815 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5817 reply("CSMSG_NO_ACCESS");
5820 if(argv[1][0] == '*' && argv[1][1] == 0)
5822 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5824 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)))
5826 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5829 else if(new_modes->argc > 1)
5831 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5832 mod_chanmode_free(new_modes);
5837 channel->channel_info->modes = *new_modes;
5838 modcmd_chanmode_announce(new_modes);
5839 mod_chanmode_free(new_modes);
5843 mod_chanmode_format(&channel->channel_info->modes, modes);
5845 reply("CSMSG_SET_MODES", modes);
5847 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5851 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5853 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5855 struct chanData *cData = channel->channel_info;
5860 /* Set flag according to value. */
5861 if(enabled_string(argv[1]))
5863 cData->flags |= mask;
5866 else if(disabled_string(argv[1]))
5868 cData->flags &= ~mask;
5873 reply("MSG_INVALID_BINARY", argv[1]);
5879 /* Find current option value. */
5880 value = (cData->flags & mask) ? 1 : 0;
5884 reply(name, user_find_message(user, "MSG_ON"));
5886 reply(name, user_find_message(user, "MSG_OFF"));
5890 static MODCMD_FUNC(chan_opt_nodelete)
5892 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5894 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5898 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5901 static MODCMD_FUNC(chan_opt_dynlimit)
5903 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5906 static MODCMD_FUNC(chan_opt_advtopic)
5908 CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5911 static MODCMD_FUNC(chan_opt_offchannel)
5913 struct chanData *cData = channel->channel_info;
5918 /* Set flag according to value. */
5919 if(enabled_string(argv[1]))
5921 if(!IsOffChannel(cData))
5922 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5923 cData->flags |= CHANNEL_OFFCHANNEL;
5926 else if(disabled_string(argv[1]))
5928 if(IsOffChannel(cData))
5930 struct mod_chanmode change;
5931 mod_chanmode_init(&change);
5933 change.args[0].mode = MODE_CHANOP;
5934 change.args[0].u.member = AddChannelUser(chanserv, channel);
5935 mod_chanmode_announce(chanserv, channel, &change);
5937 cData->flags &= ~CHANNEL_OFFCHANNEL;
5942 reply("MSG_INVALID_BINARY", argv[1]);
5948 /* Find current option value. */
5949 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5953 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5955 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5959 static MODCMD_FUNC(chan_opt_unreviewed)
5961 struct chanData *cData = channel->channel_info;
5962 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5968 /* The two directions can have different ACLs. */
5969 if(enabled_string(argv[1]))
5971 else if(disabled_string(argv[1]))
5975 reply("MSG_INVALID_BINARY", argv[1]);
5979 if (new_value != value)
5981 struct svccmd *subcmd;
5982 char subcmd_name[32];
5984 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5985 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5988 reply("MSG_COMMAND_DISABLED", subcmd_name);
5991 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5995 cData->flags |= CHANNEL_UNREVIEWED;
5998 free(cData->registrar);
5999 cData->registrar = strdup(user->handle_info->handle);
6000 cData->flags &= ~CHANNEL_UNREVIEWED;
6007 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6009 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6013 static MODCMD_FUNC(chan_opt_defaults)
6015 struct userData *uData;
6016 struct chanData *cData;
6017 const char *confirm;
6018 enum levelOption lvlOpt;
6019 enum charOption chOpt;
6021 cData = channel->channel_info;
6022 uData = GetChannelUser(cData, user->handle_info);
6023 if(!uData || (uData->access < UL_OWNER))
6025 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6028 confirm = make_confirmation_string(uData);
6029 if((argc < 2) || strcmp(argv[1], confirm))
6031 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6034 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6035 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6036 cData->modes = chanserv_conf.default_modes;
6037 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6038 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6039 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6040 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6041 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6046 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6048 struct chanData *cData = channel->channel_info;
6049 struct userData *uData;
6050 unsigned short value;
6054 if(!check_user_level(channel, user, option, 1, 1))
6056 reply("CSMSG_CANNOT_SET");
6059 value = user_level_from_name(argv[1], UL_OWNER+1);
6060 if(!value && strcmp(argv[1], "0"))
6062 reply("CSMSG_INVALID_ACCESS", argv[1]);
6065 uData = GetChannelUser(cData, user->handle_info);
6066 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6068 reply("CSMSG_BAD_SETLEVEL");
6074 if(value > cData->lvlOpts[lvlGiveOps])
6076 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6081 if(value < cData->lvlOpts[lvlGiveVoice])
6083 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6088 /* This test only applies to owners, since non-owners
6089 * trying to set an option to above their level get caught
6090 * by the CSMSG_BAD_SETLEVEL test above.
6092 if(value > uData->access)
6094 reply("CSMSG_BAD_SETTERS");
6101 cData->lvlOpts[option] = value;
6103 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6107 static MODCMD_FUNC(chan_opt_enfops)
6109 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6112 static MODCMD_FUNC(chan_opt_giveops)
6114 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6117 static MODCMD_FUNC(chan_opt_enfmodes)
6119 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6122 static MODCMD_FUNC(chan_opt_enftopic)
6124 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6127 static MODCMD_FUNC(chan_opt_pubcmd)
6129 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6132 static MODCMD_FUNC(chan_opt_setters)
6134 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6137 static MODCMD_FUNC(chan_opt_ctcpusers)
6139 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6142 static MODCMD_FUNC(chan_opt_userinfo)
6144 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6147 static MODCMD_FUNC(chan_opt_givevoice)
6149 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6152 static MODCMD_FUNC(chan_opt_topicsnarf)
6154 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6157 static MODCMD_FUNC(chan_opt_vote)
6159 return channel_level_option(lvlVote, CSFUNC_ARGS);
6162 static MODCMD_FUNC(chan_opt_inviteme)
6164 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6168 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6170 struct chanData *cData = channel->channel_info;
6171 int count = charOptions[option].count, idx;
6175 idx = atoi(argv[1]);
6177 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6179 reply("CSMSG_INVALID_NUMERIC", idx);
6180 /* Show possible values. */
6181 for(idx = 0; idx < count; idx++)
6182 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6186 cData->chOpts[option] = charOptions[option].values[idx].value;
6190 /* Find current option value. */
6193 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6197 /* Somehow, the option value is corrupt; reset it to the default. */
6198 cData->chOpts[option] = charOptions[option].default_value;
6203 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6207 static MODCMD_FUNC(chan_opt_protect)
6209 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6212 static MODCMD_FUNC(chan_opt_toys)
6214 return channel_multiple_option(chToys, CSFUNC_ARGS);
6217 static MODCMD_FUNC(chan_opt_ctcpreaction)
6219 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6222 static MODCMD_FUNC(chan_opt_topicrefresh)
6224 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6227 static struct svccmd_list set_shows_list;
6230 handle_svccmd_unbind(struct svccmd *target) {
6232 for(ii=0; ii<set_shows_list.used; ++ii)
6233 if(target == set_shows_list.list[ii])
6234 set_shows_list.used = 0;
6237 static CHANSERV_FUNC(cmd_set)
6239 struct svccmd *subcmd;
6243 /* Check if we need to (re-)initialize set_shows_list. */
6244 if(!set_shows_list.used)
6246 if(!set_shows_list.size)
6248 set_shows_list.size = chanserv_conf.set_shows->used;
6249 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6251 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6253 const char *name = chanserv_conf.set_shows->list[ii];
6254 sprintf(buf, "%s %s", argv[0], name);
6255 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6258 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6261 svccmd_list_append(&set_shows_list, subcmd);
6267 reply("CSMSG_CHANNEL_OPTIONS");
6268 for(ii = 0; ii < set_shows_list.used; ii++)
6270 subcmd = set_shows_list.list[ii];
6271 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6276 sprintf(buf, "%s %s", argv[0], argv[1]);
6277 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6280 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6283 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6285 reply("CSMSG_NO_ACCESS");
6291 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6295 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6297 struct userData *uData;
6299 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6302 reply("CSMSG_NOT_USER", channel->name);
6308 /* Just show current option value. */
6310 else if(enabled_string(argv[1]))
6312 uData->flags |= mask;
6314 else if(disabled_string(argv[1]))
6316 uData->flags &= ~mask;
6320 reply("MSG_INVALID_BINARY", argv[1]);
6324 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6328 static MODCMD_FUNC(user_opt_noautoop)
6330 struct userData *uData;
6332 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6335 reply("CSMSG_NOT_USER", channel->name);
6338 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6339 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6341 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6344 static MODCMD_FUNC(user_opt_autoinvite)
6346 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6348 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6350 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6353 static MODCMD_FUNC(user_opt_info)
6355 struct userData *uData;
6358 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6362 /* If they got past the command restrictions (which require access)
6363 * but fail this test, we have some fool with security override on.
6365 reply("CSMSG_NOT_USER", channel->name);
6372 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6373 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6375 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6378 bp = strcspn(infoline, "\001");
6381 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6386 if(infoline[0] == '*' && infoline[1] == 0)
6389 uData->info = strdup(infoline);
6392 reply("CSMSG_USET_INFO", uData->info);
6394 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6398 struct svccmd_list uset_shows_list;
6400 static CHANSERV_FUNC(cmd_uset)
6402 struct svccmd *subcmd;
6406 /* Check if we need to (re-)initialize uset_shows_list. */
6407 if(!uset_shows_list.used)
6411 "NoAutoOp", "AutoInvite", "Info"
6414 if(!uset_shows_list.size)
6416 uset_shows_list.size = ArrayLength(options);
6417 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6419 for(ii = 0; ii < ArrayLength(options); ii++)
6421 const char *name = options[ii];
6422 sprintf(buf, "%s %s", argv[0], name);
6423 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6426 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6429 svccmd_list_append(&uset_shows_list, subcmd);
6435 /* Do this so options are presented in a consistent order. */
6436 reply("CSMSG_USER_OPTIONS");
6437 for(ii = 0; ii < uset_shows_list.used; ii++)
6438 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6442 sprintf(buf, "%s %s", argv[0], argv[1]);
6443 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6446 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6450 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6453 static CHANSERV_FUNC(cmd_giveownership)
6455 struct handle_info *new_owner_hi;
6456 struct userData *new_owner;
6457 struct userData *curr_user;
6458 struct userData *invoker;
6459 struct chanData *cData = channel->channel_info;
6460 struct do_not_register *dnr;
6461 const char *confirm;
6462 struct giveownership *giveownership;
6463 unsigned int force, override;
6464 unsigned short co_access, new_owner_old_access;
6465 char reason[MAXLEN], transfer_reason[MAXLEN];
6468 curr_user = GetChannelAccess(cData, user->handle_info);
6469 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6471 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6472 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6473 && (uData->access > 500)
6474 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6475 || uData->access < 500));
6477 if(!curr_user || (curr_user->access != UL_OWNER))
6479 struct userData *owner = NULL;
6480 for(curr_user = channel->channel_info->users;
6482 curr_user = curr_user->next)
6484 if(curr_user->access != UL_OWNER)
6488 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6495 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6497 char delay[INTERVALLEN];
6498 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6499 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6502 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6504 if(new_owner_hi == user->handle_info)
6506 reply("CSMSG_NO_TRANSFER_SELF");
6509 new_owner = GetChannelAccess(cData, new_owner_hi);
6514 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6518 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6522 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6524 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6527 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6528 if(!IsHelping(user))
6529 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6531 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6534 invoker = GetChannelUser(cData, user->handle_info);
6535 if(invoker->access <= UL_OWNER)
6537 confirm = make_confirmation_string(curr_user);
6538 if((argc < 3) || strcmp(argv[2], confirm))
6540 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6544 new_owner_old_access = new_owner->access;
6545 if(new_owner->access >= UL_COOWNER)
6546 co_access = new_owner->access;
6548 co_access = UL_COOWNER;
6549 new_owner->access = UL_OWNER;
6551 curr_user->access = co_access;
6552 cData->ownerTransfer = now;
6553 giveownership = calloc(1, sizeof(*giveownership));
6554 giveownership->issued = now;
6555 giveownership->old_owner = curr_user->handle->handle;
6556 giveownership->target = new_owner_hi->handle;
6557 giveownership->target_access = new_owner_old_access;
6560 if(argc > (2 + force))
6562 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6563 giveownership->reason = strdup(transfer_reason);
6565 giveownership->staff_issuer = strdup(user->handle_info->handle);
6568 giveownership->previous = channel->channel_info->giveownership;
6569 channel->channel_info->giveownership = giveownership;
6570 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6571 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6572 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6576 static CHANSERV_FUNC(cmd_suspend)
6578 struct handle_info *hi;
6579 struct userData *actor, *real_actor, *target;
6580 unsigned int override = 0;
6583 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6584 actor = GetChannelUser(channel->channel_info, user->handle_info);
6585 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6586 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6588 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6591 if(target->access >= actor->access)
6593 reply("MSG_USER_OUTRANKED", hi->handle);
6596 if(target->flags & USER_SUSPENDED)
6598 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6603 target->present = 0;
6606 if(!real_actor || target->access >= real_actor->access)
6607 override = CMD_LOG_OVERRIDE;
6608 target->flags |= USER_SUSPENDED;
6609 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6610 return 1 | override;
6613 static CHANSERV_FUNC(cmd_unsuspend)
6615 struct handle_info *hi;
6616 struct userData *actor, *real_actor, *target;
6617 unsigned int override = 0;
6620 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6621 actor = GetChannelUser(channel->channel_info, user->handle_info);
6622 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6623 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6625 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6628 if(target->access >= actor->access)
6630 reply("MSG_USER_OUTRANKED", hi->handle);
6633 if(!(target->flags & USER_SUSPENDED))
6635 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6638 if(!real_actor || target->access >= real_actor->access)
6639 override = CMD_LOG_OVERRIDE;
6640 target->flags &= ~USER_SUSPENDED;
6641 scan_user_presence(target, NULL);
6642 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6643 return 1 | override;
6646 static MODCMD_FUNC(cmd_deleteme)
6648 struct handle_info *hi;
6649 struct userData *target;
6650 const char *confirm_string;
6651 unsigned short access_level;
6654 hi = user->handle_info;
6655 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6657 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6660 if(target->access == UL_OWNER)
6662 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6665 confirm_string = make_confirmation_string(target);
6666 if((argc < 2) || strcmp(argv[1], confirm_string))
6668 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6671 access_level = target->access;
6672 channel_name = strdup(channel->name);
6673 del_channel_user(target, 1);
6674 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6679 static CHANSERV_FUNC(cmd_addvote)
6681 struct chanData *cData = channel->channel_info;
6682 struct userData *uData, *target;
6683 struct handle_info *hi;
6684 if (!cData) return 0;
6686 hi = user->handle_info;
6687 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6689 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6692 if(target->access < 300) {
6693 reply("CSMSG_NO_ACCESS");
6697 reply("CSMSG_ADDVOTE_FULL");
6701 msg = unsplit_string(argv + 1, argc - 1, NULL);
6702 cData->vote = strdup(msg);
6703 cData->vote_start=0;
6704 dict_delete(cData->vote_options);
6705 cData->vote_options = dict_new();
6706 dict_set_free_data(cData->vote_options, free_vote_options);
6707 for(uData = channel->channel_info->users; uData; uData = uData->next)
6712 reply("CSMSG_ADDVOTE_DONE");
6716 static CHANSERV_FUNC(cmd_delvote)
6718 struct chanData *cData = channel->channel_info;
6719 struct userData *target;
6720 struct handle_info *hi;
6721 if (!cData) return 0;
6722 hi = user->handle_info;
6723 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6725 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6728 if(target->access < 300) {
6729 reply("CSMSG_NO_ACCESS");
6733 reply("CSMSG_NO_VOTE");
6738 reply("CSMSG_DELVOTE_DONE");
6742 static CHANSERV_FUNC(cmd_addoption)
6744 struct chanData *cData = channel->channel_info;
6745 struct userData *target;
6746 struct handle_info *hi;
6747 if (!cData) return 0;
6749 hi = user->handle_info;
6750 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6752 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6755 if(target->access < 300) {
6756 reply("CSMSG_NO_ACCESS");
6760 reply("CSMSG_NO_VOTE");
6766 msg = unsplit_string(argv + 1, argc - 1, NULL);
6769 unsigned int lastid = 1;
6770 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6771 struct vote_option *cvOpt = iter_data(it);
6772 if(cvOpt->option_id > lastid)
6773 lastid = cvOpt->option_id;
6775 struct vote_option *vOpt;
6776 vOpt = calloc(1, sizeof(*vOpt));
6777 vOpt->name = strdup(msg);
6778 vOpt->option_id = (lastid + 1);
6780 sprintf(str,"%i",(lastid + 1));
6781 vOpt->option_str = strdup(str);
6783 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6785 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6789 static CHANSERV_FUNC(cmd_deloption)
6791 struct chanData *cData = channel->channel_info;
6792 struct userData *uData, *target;
6793 struct handle_info *hi;
6794 if (!cData) return 0;
6796 hi = user->handle_info;
6797 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6799 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6802 if(target->access < 300) {
6803 reply("CSMSG_NO_ACCESS");
6807 reply("CSMSG_NO_VOTE");
6810 if(cData->vote_start) {
6811 if(dict_size(cData->vote_options) < 3) {
6812 reply("CSMSG_VOTE_NEED_OPTIONS");
6817 int find_id = atoi(argv[1]);
6819 unsigned int found = 0;
6822 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6824 if (find_id == ii) {
6825 struct vote_option *vOpt = iter_data(it);
6826 found = vOpt->option_id;
6828 sprintf(str,"%i",vOpt->option_id);
6829 dict_remove(cData->vote_options, str);
6834 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6835 if(uData->votefor == found) {
6840 reply("CSMSG_DELOPTION_DONE");
6843 reply("CSMSG_DELOPTION_NONE");
6848 static CHANSERV_FUNC(cmd_vote)
6850 struct chanData *cData = channel->channel_info;
6851 struct userData *target;
6852 struct handle_info *hi;
6853 unsigned int votedfor = 0;
6854 char *votedfor_str = NULL;
6856 if (!cData || !cData->vote) {
6857 reply("CSMSG_NO_VOTE");
6860 if(argc > 1 && cData->vote_start) {
6861 hi = user->handle_info;
6862 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6864 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6867 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6868 reply("CSMSG_NO_ACCESS");
6872 reply("CSMSG_VOTE_VOTED");
6875 int find_id = atoi(argv[1]);
6878 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6880 if (find_id == ii) {
6881 struct vote_option *vOpt = iter_data(it);
6884 target->votefor = vOpt->option_id;
6885 votedfor = vOpt->option_id;
6886 votedfor_str = vOpt->name;
6890 reply("CSMSG_VOTE_INVALID");
6894 if (!cData->vote_start) {
6895 reply("CSMSG_VOTE_NOT_STARTED");
6897 reply("CSMSG_VOTE_QUESTION",cData->vote);
6899 unsigned int voteid = 0;
6902 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6903 struct vote_option *vOpt = iter_data(it);
6905 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6907 if(argc > 1 && cData->vote_start && votedfor_str) {
6908 reply("CSMSG_VOTE_DONE",votedfor_str);
6913 static CHANSERV_FUNC(cmd_startvote)
6915 struct chanData *cData = channel->channel_info;
6916 struct userData *target;
6917 struct handle_info *hi;
6918 if (!cData) return 0;
6919 hi = user->handle_info;
6920 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6922 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6925 if(target->access < 300) {
6926 reply("CSMSG_NO_ACCESS");
6930 reply("CSMSG_NO_VOTE");
6933 if(cData->vote_start) {
6934 reply("CSMSG_STARTVOTE_RUNNING");
6937 if(dict_size(cData->vote_options) < 2) {
6938 reply("CSMSG_VOTE_NEED_OPTIONS");
6941 cData->vote_start = 1;
6942 char response[MAXLEN];
6943 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6944 irc_privmsg(cmd->parent->bot, channel->name, response);
6945 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6946 irc_privmsg(cmd->parent->bot, channel->name, response);
6947 unsigned int voteid = 0;
6949 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6950 struct vote_option *vOpt = iter_data(it);
6952 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6953 irc_privmsg(cmd->parent->bot, channel->name, response);
6955 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6956 irc_privmsg(cmd->parent->bot, channel->name, response);
6957 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6958 irc_privmsg(cmd->parent->bot, channel->name, response);
6962 static CHANSERV_FUNC(cmd_endvote)
6964 struct chanData *cData = channel->channel_info;
6965 struct userData *target;
6966 struct handle_info *hi;
6967 if (!cData) return 0;
6968 hi = user->handle_info;
6969 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6971 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6974 if(target->access < 300) {
6975 reply("CSMSG_NO_ACCESS");
6979 reply("CSMSG_NO_VOTE");
6982 if(!cData->vote_start) {
6983 reply("CSMSG_ENDVOTE_STOPPED");
6986 cData->vote_start = 0;
6987 reply("CSMSG_ENDVOTE_DONE");
6991 static CHANSERV_FUNC(cmd_voteresults)
6993 struct chanData *cData = channel->channel_info;
6994 struct userData *target;
6995 struct handle_info *hi;
6996 if (!cData) return 0;
6998 reply("CSMSG_NO_VOTE");
7001 if (argc > 1 && !irccasecmp(argv[1], "*")) {
7002 hi = user->handle_info;
7003 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7005 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7008 if(target->access < 300) {
7009 reply("CSMSG_NO_ACCESS");
7012 char response[MAXLEN];
7013 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7014 irc_privmsg(cmd->parent->bot, channel->name, response);
7015 unsigned int voteid = 0;
7017 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7018 struct vote_option *vOpt = iter_data(it);
7020 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7021 irc_privmsg(cmd->parent->bot, channel->name, response);
7024 reply("CSMSG_VOTE_QUESTION",cData->vote);
7025 unsigned int voteid = 0;
7027 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7028 struct vote_option *vOpt = iter_data(it);
7030 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7037 chanserv_refresh_topics(UNUSED_ARG(void *data))
7039 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7040 struct chanData *cData;
7043 for(cData = channelList; cData; cData = cData->next)
7045 if(IsSuspended(cData))
7047 opt = cData->chOpts[chTopicRefresh];
7050 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7053 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7054 cData->last_refresh = refresh_num;
7056 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7059 static CHANSERV_FUNC(cmd_unf)
7063 char response[MAXLEN];
7064 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7065 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7066 irc_privmsg(cmd->parent->bot, channel->name, response);
7069 reply("CSMSG_UNF_RESPONSE");
7073 static CHANSERV_FUNC(cmd_ping)
7077 char response[MAXLEN];
7078 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7079 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7080 irc_privmsg(cmd->parent->bot, channel->name, response);
7083 reply("CSMSG_PING_RESPONSE");
7087 static CHANSERV_FUNC(cmd_wut)
7091 char response[MAXLEN];
7092 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7093 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
7094 irc_privmsg(cmd->parent->bot, channel->name, response);
7097 reply("CSMSG_WUT_RESPONSE");
7101 static CHANSERV_FUNC(cmd_8ball)
7103 unsigned int i, j, accum;
7108 for(i=1; i<argc; i++)
7109 for(j=0; argv[i][j]; j++)
7110 accum = (accum << 5) - accum + toupper(argv[i][j]);
7111 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7114 char response[MAXLEN];
7115 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
7116 irc_privmsg(cmd->parent->bot, channel->name, response);
7119 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7123 static CHANSERV_FUNC(cmd_d)
7125 unsigned long sides, count, modifier, ii, total;
7126 char response[MAXLEN], *sep;
7130 if((count = strtoul(argv[1], &sep, 10)) < 1)
7140 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7141 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7145 else if((sep[0] == '-') && isdigit(sep[1]))
7146 modifier = strtoul(sep, NULL, 10);
7147 else if((sep[0] == '+') && isdigit(sep[1]))
7148 modifier = strtoul(sep+1, NULL, 10);
7155 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7160 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7163 for(total = ii = 0; ii < count; ++ii)
7164 total += (rand() % sides) + 1;
7167 if((count > 1) || modifier)
7169 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7170 sprintf(response, fmt, total, count, sides, modifier);
7174 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7175 sprintf(response, fmt, total, sides);
7178 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7180 send_message_type(4, user, cmd->parent->bot, "%s", response);
7184 static CHANSERV_FUNC(cmd_huggle)
7186 /* CTCP must be via PRIVMSG, never notice */
7188 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7190 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7195 chanserv_adjust_limit(void *data)
7197 struct mod_chanmode change;
7198 struct chanData *cData = data;
7199 struct chanNode *channel = cData->channel;
7202 if(IsSuspended(cData))
7205 cData->limitAdjusted = now;
7206 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7207 if(cData->modes.modes_set & MODE_LIMIT)
7209 if(limit > cData->modes.new_limit)
7210 limit = cData->modes.new_limit;
7211 else if(limit == cData->modes.new_limit)
7215 mod_chanmode_init(&change);
7216 change.modes_set = MODE_LIMIT;
7217 change.new_limit = limit;
7218 mod_chanmode_announce(chanserv, channel, &change);
7222 handle_new_channel(struct chanNode *channel)
7224 struct chanData *cData;
7226 if(!(cData = channel->channel_info))
7229 if(cData->modes.modes_set || cData->modes.modes_clear)
7230 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7232 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7233 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7236 void handle_new_channel_created(char *chan, struct userNode *user) {
7237 if(user->handle_info && chanserv_conf.new_channel_authed) {
7238 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7239 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7240 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7242 if(chanserv_conf.new_channel_msg)
7243 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7246 /* Welcome to my worst nightmare. Warning: Read (or modify)
7247 the code below at your own risk. */
7249 handle_join(struct modeNode *mNode)
7251 struct mod_chanmode change;
7252 struct userNode *user = mNode->user;
7253 struct chanNode *channel = mNode->channel;
7254 struct chanData *cData;
7255 struct userData *uData = NULL;
7256 struct banData *bData;
7257 struct handle_info *handle;
7258 unsigned int modes = 0, info = 0;
7262 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7265 cData = channel->channel_info;
7266 if(channel->members.used > cData->max) {
7267 cData->max = channel->members.used;
7268 cData->max_time = now;
7271 for(i = 0; i < channel->invited.used; i++)
7273 if(channel->invited.list[i] == user) {
7274 userList_remove(&channel->invited, user);
7278 /* Check for bans. If they're joining through a ban, one of two
7280 * 1: Join during a netburst, by riding the break. Kick them
7281 * unless they have ops or voice in the channel.
7282 * 2: They're allowed to join through the ban (an invite in
7283 * ircu2.10, or a +e on Hybrid, or something).
7284 * If they're not joining through a ban, and the banlist is not
7285 * full, see if they're on the banlist for the channel. If so,
7288 if(user->uplink->burst && !mNode->modes)
7291 for(ii = 0; ii < channel->banlist.used; ii++)
7293 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7295 /* Riding a netburst. Naughty. */
7296 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7302 mod_chanmode_init(&change);
7304 if(channel->banlist.used < MAXBANS)
7306 /* Not joining through a ban. */
7307 for(bData = cData->bans;
7308 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7309 bData = bData->next);
7313 char kick_reason[MAXLEN];
7314 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7316 bData->triggered = now;
7317 if(bData != cData->bans)
7319 /* Shuffle the ban to the head of the list. */
7321 bData->next->prev = bData->prev;
7323 bData->prev->next = bData->next;
7326 bData->next = cData->bans;
7329 cData->bans->prev = bData;
7330 cData->bans = bData;
7333 change.args[0].mode = MODE_BAN;
7334 change.args[0].u.hostmask = bData->mask;
7335 mod_chanmode_announce(chanserv, channel, &change);
7336 KickChannelUser(user, channel, chanserv, kick_reason);
7341 /* ChanServ will not modify the limits in join-flooded channels,
7342 or when there are enough slots left below the limit. */
7343 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7344 && !channel->join_flooded
7345 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7347 /* The user count has begun "bumping" into the channel limit,
7348 so set a timer to raise the limit a bit. Any previous
7349 timers are removed so three incoming users within the delay
7350 results in one limit change, not three. */
7352 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7353 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7356 if(channel->join_flooded)
7358 /* don't automatically give ops or voice during a join flood */
7360 else if(cData->lvlOpts[lvlGiveOps] == 0)
7361 modes |= MODE_CHANOP;
7362 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7363 modes |= MODE_VOICE;
7365 greeting = cData->greeting;
7366 if(user->handle_info)
7368 handle = user->handle_info;
7370 if(IsHelper(user) && !IsHelping(user))
7373 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7375 if(channel == chanserv_conf.support_channels.list[ii])
7377 HANDLE_SET_FLAG(user->handle_info, HELPING);
7383 uData = GetTrueChannelAccess(cData, handle);
7384 if(uData && !IsUserSuspended(uData))
7386 /* Ops and above were handled by the above case. */
7387 if(IsUserAutoOp(uData))
7389 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7390 modes |= MODE_CHANOP;
7391 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7392 modes |= MODE_VOICE;
7394 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7395 cData->visited = now;
7396 if(cData->user_greeting)
7397 greeting = cData->user_greeting;
7399 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7400 && ((now - uData->seen) >= chanserv_conf.info_delay)
7408 /* If user joining normally (not during burst), apply op or voice,
7409 * and send greeting/userinfo as appropriate.
7411 if(!user->uplink->burst)
7415 if(modes & MODE_CHANOP)
7416 modes &= ~MODE_VOICE;
7417 change.args[0].mode = modes;
7418 change.args[0].u.member = mNode;
7419 mod_chanmode_announce(chanserv, channel, &change);
7422 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7423 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7424 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7430 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7432 struct mod_chanmode change;
7433 struct userData *channel;
7434 unsigned int ii, jj;
7436 if(!user->handle_info)
7439 mod_chanmode_init(&change);
7441 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7443 struct chanNode *cn;
7444 struct modeNode *mn;
7445 if(IsUserSuspended(channel)
7446 || IsSuspended(channel->channel)
7447 || !(cn = channel->channel->channel))
7450 mn = GetUserMode(cn, user);
7453 if(!IsUserSuspended(channel)
7454 && IsUserAutoInvite(channel)
7455 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7457 && !user->uplink->burst)
7458 irc_invite(chanserv, user, cn);
7462 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7463 channel->channel->visited = now;
7465 if(IsUserAutoOp(channel))
7467 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7468 change.args[0].mode = MODE_CHANOP;
7469 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7470 change.args[0].mode = MODE_VOICE;
7472 change.args[0].mode = 0;
7473 change.args[0].u.member = mn;
7474 if(change.args[0].mode)
7475 mod_chanmode_announce(chanserv, cn, &change);
7478 channel->seen = now;
7479 channel->present = 1;
7482 for(ii = 0; ii < user->channels.used; ++ii)
7484 struct chanNode *chan = user->channels.list[ii]->channel;
7485 struct banData *ban;
7487 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7488 || !chan->channel_info
7489 || IsSuspended(chan->channel_info))
7491 for(jj = 0; jj < chan->banlist.used; ++jj)
7492 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7494 if(jj < chan->banlist.used)
7496 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7498 char kick_reason[MAXLEN];
7499 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7501 change.args[0].mode = MODE_BAN;
7502 change.args[0].u.hostmask = ban->mask;
7503 mod_chanmode_announce(chanserv, chan, &change);
7504 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7505 KickChannelUser(user, chan, chanserv, kick_reason);
7506 ban->triggered = now;
7511 if(IsSupportHelper(user))
7513 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7515 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7517 HANDLE_SET_FLAG(user->handle_info, HELPING);
7525 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7527 struct chanData *cData;
7528 struct userData *uData;
7530 cData = mn->channel->channel_info;
7531 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7534 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7536 /* Allow for a bit of padding so that the limit doesn't
7537 track the user count exactly, which could get annoying. */
7538 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7540 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7541 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7545 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7547 scan_user_presence(uData, mn->user);
7549 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7550 cData->visited = now;
7553 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7556 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7557 struct chanNode *channel;
7558 struct userNode *exclude;
7559 /* When looking at the channel that is being /part'ed, we
7560 * have to skip over the client that is leaving. For
7561 * other channels, we must not do that.
7563 channel = chanserv_conf.support_channels.list[ii];
7564 exclude = (channel == mn->channel) ? mn->user : NULL;
7565 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7568 if(ii == chanserv_conf.support_channels.used)
7569 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7574 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7576 struct userData *uData;
7578 if(!channel->channel_info || !kicker || IsService(kicker)
7579 || (kicker == victim) || IsSuspended(channel->channel_info)
7580 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7583 if(protect_user(victim, kicker, channel->channel_info))
7585 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7586 KickChannelUser(kicker, channel, chanserv, reason);
7589 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7594 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7596 struct chanData *cData;
7598 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7601 cData = channel->channel_info;
7602 if(bad_topic(channel, user, channel->topic))
7604 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7605 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7606 SetChannelTopic(channel, chanserv, old_topic, 1);
7607 else if(cData->topic)
7608 SetChannelTopic(channel, chanserv, cData->topic, 1);
7611 /* With topicsnarf, grab the topic and save it as the default topic. */
7612 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7615 cData->topic = strdup(channel->topic);
7621 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7623 struct mod_chanmode *bounce = NULL;
7624 unsigned int bnc, ii;
7627 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7630 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7631 && mode_lock_violated(&channel->channel_info->modes, change))
7633 char correct[MAXLEN];
7634 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7635 mod_chanmode_format(&channel->channel_info->modes, correct);
7636 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7638 for(ii = bnc = 0; ii < change->argc; ++ii)
7640 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7642 const struct userNode *victim = change->args[ii].u.member->user;
7643 if(!protect_user(victim, user, channel->channel_info))
7646 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7649 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7650 bounce->args[bnc].u.member = GetUserMode(channel, user);
7651 if(bounce->args[bnc].u.member)
7655 bounce->args[bnc].mode = MODE_CHANOP;
7656 bounce->args[bnc].u.member = change->args[ii].u.member;
7658 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7660 else if(change->args[ii].mode & MODE_CHANOP)
7662 const struct userNode *victim = change->args[ii].u.member->user;
7663 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7666 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7667 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7668 bounce->args[bnc].u.member = change->args[ii].u.member;
7671 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7673 const char *ban = change->args[ii].u.hostmask;
7674 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7677 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7678 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7679 bounce->args[bnc].u.hostmask = strdup(ban);
7681 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7686 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7687 mod_chanmode_announce(chanserv, channel, bounce);
7688 for(ii = 0; ii < change->argc; ++ii)
7689 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7690 free((char*)bounce->args[ii].u.hostmask);
7691 mod_chanmode_free(bounce);
7696 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7698 struct chanNode *channel;
7699 struct banData *bData;
7700 struct mod_chanmode change;
7701 unsigned int ii, jj;
7702 char kick_reason[MAXLEN];
7704 mod_chanmode_init(&change);
7706 change.args[0].mode = MODE_BAN;
7707 for(ii = 0; ii < user->channels.used; ++ii)
7709 channel = user->channels.list[ii]->channel;
7710 /* Need not check for bans if they're opped or voiced. */
7711 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7713 /* Need not check for bans unless channel registration is active. */
7714 if(!channel->channel_info || IsSuspended(channel->channel_info))
7716 /* Look for a matching ban already on the channel. */
7717 for(jj = 0; jj < channel->banlist.used; ++jj)
7718 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7720 /* Need not act if we found one. */
7721 if(jj < channel->banlist.used)
7723 /* Look for a matching ban in this channel. */
7724 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7726 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7728 change.args[0].u.hostmask = bData->mask;
7729 mod_chanmode_announce(chanserv, channel, &change);
7730 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7731 KickChannelUser(user, channel, chanserv, kick_reason);
7732 bData->triggered = now;
7733 break; /* we don't need to check any more bans in the channel */
7738 static void handle_rename(struct handle_info *handle, const char *old_handle)
7740 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7744 dict_remove2(handle_dnrs, old_handle, 1);
7745 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7746 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7751 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7753 struct userNode *h_user;
7755 if(handle->channels)
7757 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7758 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7760 while(handle->channels)
7761 del_channel_user(handle->channels, 1);
7766 handle_server_link(UNUSED_ARG(struct server *server))
7768 struct chanData *cData;
7770 for(cData = channelList; cData; cData = cData->next)
7772 if(!IsSuspended(cData))
7773 cData->may_opchan = 1;
7774 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7775 && !cData->channel->join_flooded
7776 && ((cData->channel->limit - cData->channel->members.used)
7777 < chanserv_conf.adjust_threshold))
7779 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7780 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7786 chanserv_conf_read(void)
7790 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7791 struct mod_chanmode *change;
7792 struct string_list *strlist;
7793 struct chanNode *chan;
7796 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7798 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7801 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7802 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7803 chanserv_conf.support_channels.used = 0;
7804 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7806 for(ii = 0; ii < strlist->used; ++ii)
7808 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7811 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7813 channelList_append(&chanserv_conf.support_channels, chan);
7816 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7819 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7822 chan = AddChannel(str, now, str2, NULL);
7824 channelList_append(&chanserv_conf.support_channels, chan);
7826 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7827 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7828 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7829 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7830 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7831 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7832 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7833 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7834 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7835 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7836 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7837 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7838 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7839 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7840 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7841 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7842 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7843 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7844 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7845 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7846 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7847 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7848 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7849 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7850 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7851 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7852 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7853 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7854 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7856 NickChange(chanserv, str, 0);
7857 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7858 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7859 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7860 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7861 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7862 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7863 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7864 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7865 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7866 chanserv_conf.max_owned = str ? atoi(str) : 5;
7867 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7868 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7869 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7870 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7871 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7872 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7873 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7874 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7875 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7876 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7877 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7878 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7879 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7882 safestrncpy(mode_line, str, sizeof(mode_line));
7883 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7884 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7885 && (change->argc < 2))
7887 chanserv_conf.default_modes = *change;
7888 mod_chanmode_free(change);
7890 free_string_list(chanserv_conf.set_shows);
7891 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7893 strlist = string_list_copy(strlist);
7896 static const char *list[] = {
7897 /* free form text */
7898 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7899 /* options based on user level */
7900 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7901 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7902 /* multiple choice options */
7903 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7904 /* binary options */
7905 "DynLimit", "NoDelete", "expire", "Vote",
7909 strlist = alloc_string_list(ArrayLength(list)-1);
7910 for(ii=0; list[ii]; ii++)
7911 string_list_append(strlist, strdup(list[ii]));
7913 chanserv_conf.set_shows = strlist;
7914 /* We don't look things up now, in case the list refers to options
7915 * defined by modules initialized after this point. Just mark the
7916 * function list as invalid, so it will be initialized.
7918 set_shows_list.used = 0;
7919 free_string_list(chanserv_conf.eightball);
7920 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7923 strlist = string_list_copy(strlist);
7927 strlist = alloc_string_list(4);
7928 string_list_append(strlist, strdup("Yes."));
7929 string_list_append(strlist, strdup("No."));
7930 string_list_append(strlist, strdup("Maybe so."));
7932 chanserv_conf.eightball = strlist;
7933 free_string_list(chanserv_conf.old_ban_names);
7934 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7936 strlist = string_list_copy(strlist);
7938 strlist = alloc_string_list(2);
7939 chanserv_conf.old_ban_names = strlist;
7940 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7941 off_channel = str ? atoi(str) : 0;
7945 chanserv_note_type_read(const char *key, struct record_data *rd)
7948 struct note_type *ntype;
7951 if(!(obj = GET_RECORD_OBJECT(rd)))
7953 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7956 if(!(ntype = chanserv_create_note_type(key)))
7958 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7962 /* Figure out set access */
7963 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7965 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7966 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7968 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7970 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7971 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7973 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7975 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7979 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7980 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7981 ntype->set_access.min_opserv = 0;
7984 /* Figure out visibility */
7985 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7986 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7987 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7988 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7989 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7990 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7991 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7992 ntype->visible_type = NOTE_VIS_ALL;
7994 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7996 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7997 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8001 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8003 struct vote_option *vOpt;
8006 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8008 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8012 vOpt = calloc(1, sizeof(*vOpt));
8013 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8014 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8015 vOpt->voted = str ? atoi(str) : 0;
8016 vOpt->option_id = str ? atoi(key) : 0;
8017 vOpt->option_str = strdup(key);
8018 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8022 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8024 struct handle_info *handle;
8025 struct userData *uData;
8026 char *seen, *inf, *flags, *voted, *votefor;
8027 unsigned long last_seen;
8028 unsigned short access_level;
8030 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8032 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8036 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8037 if(access_level > UL_OWNER)
8039 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8043 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8044 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8045 last_seen = seen ? strtoul(seen, NULL, 0) : now;
8046 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8047 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8048 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8049 handle = get_handle_info(key);
8052 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8056 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8057 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8059 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8060 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8068 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8070 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8071 unsigned long set_time, triggered_time, expires_time;
8073 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8075 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8079 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8080 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8081 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8082 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8083 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8084 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8085 if (!reason || !owner)
8088 set_time = set ? strtoul(set, NULL, 0) : now;
8089 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8091 expires_time = strtoul(s_expires, NULL, 0);
8093 expires_time = set_time + atoi(s_duration);
8097 if(!reason || (expires_time && (expires_time < now)))
8100 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8103 static struct suspended *
8104 chanserv_read_suspended(dict_t obj)
8106 struct suspended *suspended = calloc(1, sizeof(*suspended));
8110 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8111 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8112 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8113 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8114 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8115 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8116 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8117 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8118 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8119 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8123 static struct giveownership *
8124 chanserv_read_giveownership(dict_t obj)
8126 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8130 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8131 giveownership->staff_issuer = str ? strdup(str) : NULL;
8133 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8135 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8136 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8138 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8139 giveownership->reason = str ? strdup(str) : NULL;
8140 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8141 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8143 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8144 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8145 return giveownership;
8149 chanserv_channel_read(const char *key, struct record_data *hir)
8151 struct suspended *suspended;
8152 struct giveownership *giveownership;
8153 struct mod_chanmode *modes;
8154 struct chanNode *cNode;
8155 struct chanData *cData;
8156 struct dict *channel, *obj;
8157 char *str, *argv[10];
8161 channel = hir->d.object;
8163 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8166 cNode = AddChannel(key, now, NULL, NULL);
8169 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8172 cData = register_channel(cNode, str);
8175 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8179 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8181 enum levelOption lvlOpt;
8182 enum charOption chOpt;
8184 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8185 cData->flags = atoi(str);
8187 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8189 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8191 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8192 else if(levelOptions[lvlOpt].old_flag)
8194 if(cData->flags & levelOptions[lvlOpt].old_flag)
8195 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8197 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8201 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8203 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8205 cData->chOpts[chOpt] = str[0];
8208 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8210 enum levelOption lvlOpt;
8211 enum charOption chOpt;
8214 cData->flags = base64toint(str, 5);
8215 count = strlen(str += 5);
8216 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8219 if(levelOptions[lvlOpt].old_flag)
8221 if(cData->flags & levelOptions[lvlOpt].old_flag)
8222 lvl = levelOptions[lvlOpt].flag_value;
8224 lvl = levelOptions[lvlOpt].default_value;
8226 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8228 case 'c': lvl = UL_COOWNER; break;
8229 case 'm': lvl = UL_MASTER; break;
8230 case 'n': lvl = UL_OWNER+1; break;
8231 case 'o': lvl = UL_OP; break;
8232 case 'p': lvl = UL_PEON; break;
8233 case 'w': lvl = UL_OWNER; break;
8234 default: lvl = 0; break;
8236 cData->lvlOpts[lvlOpt] = lvl;
8238 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8239 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8242 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8244 cData->expiry = atoi(str);
8245 if(cData->expiry > 0) {
8246 if(cData->expiry > now) {
8247 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8249 timeq_add(1, chanserv_expire_channel, cData);
8256 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8258 suspended = chanserv_read_suspended(obj);
8259 cData->suspended = suspended;
8260 suspended->cData = cData;
8261 /* We could use suspended->expires and suspended->revoked to
8262 * set the CHANNEL_SUSPENDED flag, but we don't. */
8264 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8266 suspended = calloc(1, sizeof(*suspended));
8267 suspended->issued = 0;
8268 suspended->revoked = 0;
8269 suspended->suspender = strdup(str);
8270 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8271 suspended->expires = str ? atoi(str) : 0;
8272 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8273 suspended->reason = strdup(str ? str : "No reason");
8274 suspended->previous = NULL;
8275 cData->suspended = suspended;
8276 suspended->cData = cData;
8280 cData->flags &= ~CHANNEL_SUSPENDED;
8281 suspended = NULL; /* to squelch a warning */
8284 if(IsSuspended(cData)) {
8285 if(suspended->expires > now)
8286 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8287 else if(suspended->expires)
8288 cData->flags &= ~CHANNEL_SUSPENDED;
8291 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8293 giveownership = chanserv_read_giveownership(obj);
8294 cData->giveownership = giveownership;
8297 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8298 struct mod_chanmode change;
8299 mod_chanmode_init(&change);
8301 change.args[0].mode = MODE_CHANOP;
8302 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8303 mod_chanmode_announce(chanserv, cNode, &change);
8306 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8307 cData->registered = str ? strtoul(str, NULL, 0) : now;
8308 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8309 cData->visited = str ? strtoul(str, NULL, 0) : now;
8310 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8311 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8312 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8313 cData->max = str ? atoi(str) : 0;
8314 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8315 cData->max_time = str ? atoi(str) : 0;
8316 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8317 cData->greeting = str ? strdup(str) : NULL;
8318 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8319 cData->user_greeting = str ? strdup(str) : NULL;
8320 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8321 cData->topic_mask = str ? strdup(str) : NULL;
8322 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8323 cData->topic = str ? strdup(str) : NULL;
8325 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8327 cData->vote = str ? strdup(str) : NULL;
8328 dict_delete(cData->vote_options);
8329 cData->vote_options = dict_new();
8330 dict_set_free_data(cData->vote_options, free_vote_options);
8331 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8332 cData->vote_start = str ? atoi(str) : 0;
8333 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8334 for(it = dict_first(obj); it; it = iter_next(it)) {
8335 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8339 obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8340 for(it = dict_first(obj); it; it = iter_next(it))
8342 struct record_data *rd = iter_data(it);
8343 if(rd->type != RECDB_QSTRING) continue;
8344 int advtopic_index = atoi(iter_key(it));
8345 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8346 cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8349 if(!IsSuspended(cData)
8350 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8351 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8352 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8353 cData->modes = *modes;
8355 cData->modes.modes_set |= MODE_REGISTERED;
8356 if(cData->modes.argc > 1)
8357 cData->modes.argc = 1;
8358 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8359 mod_chanmode_free(modes);
8362 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8363 for(it = dict_first(obj); it; it = iter_next(it))
8364 user_read_helper(iter_key(it), iter_data(it), cData);
8366 if(!cData->users && !IsProtected(cData))
8368 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8369 unregister_channel(cData, "has empty user list.");
8373 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8374 for(it = dict_first(obj); it; it = iter_next(it))
8375 ban_read_helper(iter_key(it), iter_data(it), cData);
8377 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8378 for(it = dict_first(obj); it; it = iter_next(it))
8380 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8381 struct record_data *rd = iter_data(it);
8382 const char *note, *setter;
8384 if(rd->type != RECDB_OBJECT)
8386 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8390 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8392 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8394 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8398 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8399 if(!setter) setter = "<unknown>";
8400 chanserv_add_channel_note(cData, ntype, setter, note);
8408 chanserv_dnr_read(const char *key, struct record_data *hir)
8410 const char *setter, *reason, *str;
8411 struct do_not_register *dnr;
8412 unsigned long expiry;
8414 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8417 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8420 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8423 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8426 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8427 expiry = str ? strtoul(str, NULL, 0) : 0;
8428 if(expiry && expiry <= now)
8430 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8433 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8435 dnr->set = atoi(str);
8441 chanserv_saxdb_read(struct dict *database)
8443 struct dict *section;
8446 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8447 for(it = dict_first(section); it; it = iter_next(it))
8448 chanserv_note_type_read(iter_key(it), iter_data(it));
8450 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8451 for(it = dict_first(section); it; it = iter_next(it))
8452 chanserv_channel_read(iter_key(it), iter_data(it));
8454 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8455 for(it = dict_first(section); it; it = iter_next(it))
8456 chanserv_dnr_read(iter_key(it), iter_data(it));
8462 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8464 int high_present = 0;
8465 saxdb_start_record(ctx, KEY_USERS, 1);
8466 for(; uData; uData = uData->next)
8468 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8470 saxdb_start_record(ctx, uData->handle->handle, 0);
8471 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8472 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8474 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8475 if(uData->channel->vote && uData->voted)
8476 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8477 if(uData->channel->vote && uData->votefor)
8478 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8480 saxdb_write_string(ctx, KEY_INFO, uData->info);
8481 saxdb_end_record(ctx);
8483 saxdb_end_record(ctx);
8484 return high_present;
8488 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8492 saxdb_start_record(ctx, KEY_BANS, 1);
8493 for(; bData; bData = bData->next)
8495 saxdb_start_record(ctx, bData->mask, 0);
8496 saxdb_write_int(ctx, KEY_SET, bData->set);
8497 if(bData->triggered)
8498 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8500 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8502 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8504 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8505 saxdb_end_record(ctx);
8507 saxdb_end_record(ctx);
8511 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8513 saxdb_start_record(ctx, name, 0);
8514 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8515 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8517 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8519 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8521 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8523 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8524 saxdb_end_record(ctx);
8528 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8530 saxdb_start_record(ctx, name, 0);
8531 if(giveownership->staff_issuer)
8532 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8533 if(giveownership->old_owner)
8534 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8535 if(giveownership->target)
8536 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8537 if(giveownership->target_access)
8538 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8539 if(giveownership->reason)
8540 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8541 if(giveownership->issued)
8542 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8543 if(giveownership->previous)
8544 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8545 saxdb_end_record(ctx);
8549 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8553 enum levelOption lvlOpt;
8554 enum charOption chOpt;
8557 saxdb_start_record(ctx, channel->channel->name, 1);
8559 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8560 saxdb_write_int(ctx, KEY_MAX, channel->max);
8561 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8563 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8564 if(channel->registrar)
8565 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8566 if(channel->greeting)
8567 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8568 if(channel->user_greeting)
8569 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8570 if(channel->topic_mask)
8571 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8572 if(channel->suspended)
8573 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8574 if(channel->giveownership)
8575 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8577 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8580 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8581 if(channel->vote_start)
8582 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8583 if (dict_size(channel->vote_options)) {
8584 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8585 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8586 struct vote_option *vOpt = iter_data(it);
8588 sprintf(str,"%i",vOpt->option_id);
8589 saxdb_start_record(ctx, str, 0);
8591 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8593 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8594 saxdb_end_record(ctx);
8596 saxdb_end_record(ctx);
8600 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8601 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8602 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8603 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8604 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8606 buf[0] = channel->chOpts[chOpt];
8608 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8610 saxdb_end_record(ctx);
8612 if(channel->modes.modes_set || channel->modes.modes_clear)
8614 mod_chanmode_format(&channel->modes, buf);
8615 saxdb_write_string(ctx, KEY_MODES, buf);
8618 high_present = chanserv_write_users(ctx, channel->users);
8619 chanserv_write_bans(ctx, channel->bans);
8621 if(channel->flags & CHANNEL_ADVTOPIC) {
8622 saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8624 for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8625 if(channel->advtopic[advtopic_index])
8626 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8628 saxdb_end_record(ctx);
8631 if(dict_size(channel->notes))
8635 saxdb_start_record(ctx, KEY_NOTES, 1);
8636 for(it = dict_first(channel->notes); it; it = iter_next(it))
8638 struct note *note = iter_data(it);
8639 saxdb_start_record(ctx, iter_key(it), 0);
8640 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8641 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8642 saxdb_end_record(ctx);
8644 saxdb_end_record(ctx);
8647 if(channel->ownerTransfer)
8648 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8649 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8650 saxdb_end_record(ctx);
8654 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8658 saxdb_start_record(ctx, ntype->name, 0);
8659 switch(ntype->set_access_type)
8661 case NOTE_SET_CHANNEL_ACCESS:
8662 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8664 case NOTE_SET_CHANNEL_SETTER:
8665 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8667 case NOTE_SET_PRIVILEGED: default:
8668 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8671 switch(ntype->visible_type)
8673 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8674 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8675 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8677 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8678 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8679 saxdb_end_record(ctx);
8683 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8685 struct do_not_register *dnr;
8686 dict_iterator_t it, next;
8688 for(it = dict_first(dnrs); it; it = next)
8690 next = iter_next(it);
8691 dnr = iter_data(it);
8692 if(dnr->expires && dnr->expires <= now)
8694 dict_remove(dnrs, iter_key(it));
8697 saxdb_start_record(ctx, dnr->chan_name, 0);
8699 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8701 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8702 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8703 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8704 saxdb_end_record(ctx);
8709 chanserv_saxdb_write(struct saxdb_context *ctx)
8712 struct chanData *channel;
8715 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8716 for(it = dict_first(note_types); it; it = iter_next(it))
8717 chanserv_write_note_type(ctx, iter_data(it));
8718 saxdb_end_record(ctx);
8721 saxdb_start_record(ctx, KEY_DNR, 1);
8722 write_dnrs_helper(ctx, handle_dnrs);
8723 write_dnrs_helper(ctx, plain_dnrs);
8724 write_dnrs_helper(ctx, mask_dnrs);
8725 saxdb_end_record(ctx);
8728 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8729 for(channel = channelList; channel; channel = channel->next)
8730 chanserv_write_channel(ctx, channel);
8731 saxdb_end_record(ctx);
8737 chanserv_db_cleanup(void) {
8739 unreg_part_func(handle_part);
8741 unregister_channel(channelList, "terminating.");
8742 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8743 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8744 free(chanserv_conf.support_channels.list);
8745 dict_delete(handle_dnrs);
8746 dict_delete(plain_dnrs);
8747 dict_delete(mask_dnrs);
8748 dict_delete(note_types);
8749 free_string_list(chanserv_conf.eightball);
8750 free_string_list(chanserv_conf.old_ban_names);
8751 free_string_list(chanserv_conf.set_shows);
8752 free(set_shows_list.list);
8753 free(uset_shows_list.list);
8756 struct userData *helper = helperList;
8757 helperList = helperList->next;
8762 #if defined(GCC_VARMACROS)
8763 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8764 #elif defined(C99_VARMACROS)
8765 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8767 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8768 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8771 init_chanserv(const char *nick)
8773 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8774 conf_register_reload(chanserv_conf_read);
8778 reg_server_link_func(handle_server_link);
8779 reg_new_channel_func(handle_new_channel);
8780 reg_join_func(handle_join);
8781 reg_part_func(handle_part);
8782 reg_kick_func(handle_kick);
8783 reg_topic_func(handle_topic);
8784 reg_mode_change_func(handle_mode);
8785 reg_nick_change_func(handle_nick_change);
8786 reg_auth_func(handle_auth);
8789 reg_handle_rename_func(handle_rename);
8790 reg_unreg_func(handle_unreg);
8792 handle_dnrs = dict_new();
8793 dict_set_free_data(handle_dnrs, free);
8794 plain_dnrs = dict_new();
8795 dict_set_free_data(plain_dnrs, free);
8796 mask_dnrs = dict_new();
8797 dict_set_free_data(mask_dnrs, free);
8799 reg_svccmd_unbind_func(handle_svccmd_unbind);
8800 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8801 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8802 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8803 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8804 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8805 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8806 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8807 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8808 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8809 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8810 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8811 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8812 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8814 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8815 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8817 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8818 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8819 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8820 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8821 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8823 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8824 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8825 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8826 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8827 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8829 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8830 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8831 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8832 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8834 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8835 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8836 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8837 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8838 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8839 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8840 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8841 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8843 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8844 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8845 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8846 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8847 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8848 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8849 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8850 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8851 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8852 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8853 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8854 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8855 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8856 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8857 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8859 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8860 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8861 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8862 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8863 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8865 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8866 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8868 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8869 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8870 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8871 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8872 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8873 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8874 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8875 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8876 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8877 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8878 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8880 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8881 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8883 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8884 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8885 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8886 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8888 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8889 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8890 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8891 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8892 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8894 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8895 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8896 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8897 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8898 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8899 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8901 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8902 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8903 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8904 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8905 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8906 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8907 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8908 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8910 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8912 /* Channel options */
8913 DEFINE_CHANNEL_OPTION(defaulttopic);
8914 DEFINE_CHANNEL_OPTION(topicmask);
8915 DEFINE_CHANNEL_OPTION(greeting);
8916 DEFINE_CHANNEL_OPTION(usergreeting);
8917 DEFINE_CHANNEL_OPTION(modes);
8918 DEFINE_CHANNEL_OPTION(enfops);
8919 DEFINE_CHANNEL_OPTION(giveops);
8920 DEFINE_CHANNEL_OPTION(protect);
8921 DEFINE_CHANNEL_OPTION(enfmodes);
8922 DEFINE_CHANNEL_OPTION(enftopic);
8923 DEFINE_CHANNEL_OPTION(pubcmd);
8924 DEFINE_CHANNEL_OPTION(givevoice);
8925 DEFINE_CHANNEL_OPTION(userinfo);
8926 DEFINE_CHANNEL_OPTION(dynlimit);
8927 DEFINE_CHANNEL_OPTION(topicsnarf);
8928 DEFINE_CHANNEL_OPTION(vote);
8929 DEFINE_CHANNEL_OPTION(nodelete);
8930 DEFINE_CHANNEL_OPTION(toys);
8931 DEFINE_CHANNEL_OPTION(setters);
8932 DEFINE_CHANNEL_OPTION(topicrefresh);
8933 DEFINE_CHANNEL_OPTION(ctcpusers);
8934 DEFINE_CHANNEL_OPTION(ctcpreaction);
8935 DEFINE_CHANNEL_OPTION(inviteme);
8936 DEFINE_CHANNEL_OPTION(advtopic);
8937 DEFINE_CHANNEL_OPTION(unreviewed);
8938 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8939 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8940 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8942 DEFINE_CHANNEL_OPTION(offchannel);
8943 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8945 /* Alias set topic to set defaulttopic for compatibility. */
8946 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8949 DEFINE_USER_OPTION(noautoop);
8950 DEFINE_USER_OPTION(autoinvite);
8951 DEFINE_USER_OPTION(info);
8953 /* Alias uset autovoice to uset autoop. */
8954 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8956 note_types = dict_new();
8957 dict_set_free_data(note_types, chanserv_deref_note_type);
8960 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8961 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8962 service_register(chanserv)->trigger = '!';
8963 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8965 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8967 if(chanserv_conf.channel_expire_frequency)
8968 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8970 if(chanserv_conf.dnr_expire_frequency)
8971 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8973 if(chanserv_conf.refresh_period)
8975 unsigned long next_refresh;
8976 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8977 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8980 reg_exit_func(chanserv_db_cleanup);
8981 message_register_table(msgtab);