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() */
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"
61 /* ChanServ database */
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
65 /* Note type parameters */
66 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
67 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
68 #define KEY_NOTE_SETTER_ACCESS "setter_access"
69 #define KEY_NOTE_VISIBILITY "visibility"
70 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
71 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
72 #define KEY_NOTE_VIS_ALL "all"
73 #define KEY_NOTE_MAX_LENGTH "max_length"
74 #define KEY_NOTE_SETTER "setter"
75 #define KEY_NOTE_NOTE "note"
77 /* Do-not-register channels */
79 #define KEY_DNR_SET "set"
80 #define KEY_DNR_SETTER "setter"
81 #define KEY_DNR_REASON "reason"
84 #define KEY_REGISTERED "registered"
85 #define KEY_REGISTRAR "registrar"
86 #define KEY_SUSPENDED "suspended"
87 #define KEY_PREVIOUS "previous"
88 #define KEY_SUSPENDER "suspender"
89 #define KEY_ISSUED "issued"
90 #define KEY_REVOKED "revoked"
91 #define KEY_SUSPEND_EXPIRES "suspend_expires"
92 #define KEY_SUSPEND_REASON "suspend_reason"
93 #define KEY_VISITED "visited"
94 #define KEY_TOPIC "topic"
95 #define KEY_GREETING "greeting"
96 #define KEY_USER_GREETING "user_greeting"
97 #define KEY_MODES "modes"
98 #define KEY_FLAGS "flags"
99 #define KEY_OPTIONS "options"
100 #define KEY_USERS "users"
101 #define KEY_BANS "bans"
102 #define KEY_MAX "max"
103 #define KEY_NOTES "notes"
104 #define KEY_TOPIC_MASK "topic_mask"
105 #define KEY_OWNER_TRANSFER "owner_transfer"
108 #define KEY_LEVEL "level"
109 #define KEY_INFO "info"
110 #define KEY_SEEN "seen"
113 #define KEY_OWNER "owner"
114 #define KEY_REASON "reason"
115 #define KEY_SET "set"
116 #define KEY_DURATION "duration"
117 #define KEY_EXPIRES "expires"
118 #define KEY_TRIGGERED "triggered"
120 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
121 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
122 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
124 /* Administrative messages */
125 static const struct message_entry msgtab[] = {
126 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
128 /* Channel registration */
129 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
130 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
131 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
132 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
133 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
134 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
136 /* Do-not-register channels */
137 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
138 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
139 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
140 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
141 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
142 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
143 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
144 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
145 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
146 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
147 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
148 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
149 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
150 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
152 /* Channel unregistration */
153 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
154 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
155 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
156 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
159 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
160 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
162 /* Channel merging */
163 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
164 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
165 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
166 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
167 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
169 /* Handle unregistration */
170 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
173 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
174 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
175 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
176 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
177 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
178 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
179 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
180 { "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." },
181 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
182 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
183 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
184 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
185 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
186 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
188 /* Removing yourself from a channel. */
189 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
190 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
191 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
193 /* User management */
194 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
195 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
196 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
197 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
198 { "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." },
199 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
200 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
201 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
203 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
204 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
205 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
206 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
207 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
208 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
209 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
212 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
213 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
214 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
215 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
216 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
217 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
218 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
219 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
220 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
221 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
222 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
223 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
224 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
225 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
226 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
227 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
229 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
231 /* Channel management */
232 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
233 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
234 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
236 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
237 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
238 { "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" },
239 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
240 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
241 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
242 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
244 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
245 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
246 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
247 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
248 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
249 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
250 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
251 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
252 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
253 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
254 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
255 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
256 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
257 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
258 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
259 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
260 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
261 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
262 { "CSMSG_SET_MODES", "$bModes $b %s" },
263 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
264 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
265 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
266 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
267 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
268 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
269 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
270 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
271 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
272 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
273 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
274 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
275 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
276 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
277 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
278 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
279 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
280 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
281 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
282 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
283 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
284 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
285 { "CSMSG_USET_INFO", "$bInfo $b %s" },
287 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
288 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
289 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
290 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
291 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
292 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
293 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
294 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
295 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
296 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
297 { "CSMSG_PROTECT_NONE", "No users will be protected." },
298 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
299 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
300 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
301 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
302 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
303 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
304 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
305 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
306 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
307 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
308 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
309 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
311 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
312 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
313 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
314 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
315 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
316 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
317 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
318 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
320 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
321 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
322 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
324 /* Channel userlist */
325 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
326 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
327 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
328 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
330 /* Channel note list */
331 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
332 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
333 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
334 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
335 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
336 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
337 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
338 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
339 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
340 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
341 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
342 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
343 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
344 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
345 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
347 /* Channel [un]suspension */
348 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
349 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
350 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
351 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
352 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
353 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
354 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
356 /* Access information */
357 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
358 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
359 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
360 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
361 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
362 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
363 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
364 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
365 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
366 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
367 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
368 { "CSMSG_UC_H_TITLE", "network helper" },
369 { "CSMSG_LC_H_TITLE", "support helper" },
370 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
372 /* Seen information */
373 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
374 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
375 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
376 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
378 /* Names information */
379 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
380 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
382 /* Channel information */
383 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
384 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
385 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
386 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
387 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
388 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
389 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
390 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
391 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
392 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
393 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
394 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
400 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
401 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
402 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
403 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
405 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
406 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
407 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
408 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
409 { "CSMSG_PEEK_OPS", "$bOps:$b" },
410 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
412 /* Network information */
413 { "CSMSG_NETWORK_INFO", "Network Information:" },
414 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
415 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
416 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
417 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
418 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
419 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
420 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
421 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
424 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
425 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
426 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
428 /* Channel searches */
429 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
430 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
431 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
432 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
434 /* Channel configuration */
435 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
436 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
437 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
438 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
441 { "CSMSG_USER_OPTIONS", "User Options:" },
442 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
445 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
446 { "CSMSG_PING_RESPONSE", "Pong!" },
447 { "CSMSG_WUT_RESPONSE", "wut" },
448 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
449 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
450 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
451 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
452 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
453 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
454 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
457 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
461 /* eject_user and unban_user flags */
462 #define ACTION_KICK 0x0001
463 #define ACTION_BAN 0x0002
464 #define ACTION_ADD_BAN 0x0004
465 #define ACTION_ADD_TIMED_BAN 0x0008
466 #define ACTION_UNBAN 0x0010
467 #define ACTION_DEL_BAN 0x0020
469 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
470 #define MODELEN 40 + KEYLEN
474 #define CSFUNC_ARGS user, channel, argc, argv, cmd
476 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
477 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
478 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
479 reply("MSG_MISSING_PARAMS", argv[0]); \
483 DECLARE_LIST(dnrList, struct do_not_register *);
484 DEFINE_LIST(dnrList, struct do_not_register *)
486 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
488 struct userNode *chanserv;
491 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
492 static struct log_type *CS_LOG;
496 struct channelList support_channels;
497 struct mod_chanmode default_modes;
499 unsigned long db_backup_frequency;
500 unsigned long channel_expire_frequency;
501 unsigned long dnr_expire_frequency;
503 unsigned long info_delay;
504 unsigned long adjust_delay;
505 unsigned long channel_expire_delay;
506 unsigned int nodelete_level;
508 unsigned int adjust_threshold;
509 int join_flood_threshold;
511 unsigned int greeting_length;
512 unsigned int refresh_period;
513 unsigned int giveownership_period;
515 unsigned int max_owned;
516 unsigned int max_chan_users;
517 unsigned int max_chan_bans;
518 unsigned int max_userinfo_length;
520 struct string_list *set_shows;
521 struct string_list *eightball;
522 struct string_list *old_ban_names;
524 const char *ctcp_short_ban_duration;
525 const char *ctcp_long_ban_duration;
527 const char *irc_operator_epithet;
528 const char *network_helper_epithet;
529 const char *support_helper_epithet;
534 struct userNode *user;
535 struct userNode *bot;
536 struct chanNode *channel;
538 unsigned short lowest;
539 unsigned short highest;
540 struct userData **users;
541 struct helpfile_table table;
544 enum note_access_type
546 NOTE_SET_CHANNEL_ACCESS,
547 NOTE_SET_CHANNEL_SETTER,
551 enum note_visible_type
554 NOTE_VIS_CHANNEL_USERS,
560 enum note_access_type set_access_type;
562 unsigned int min_opserv;
563 unsigned short min_ulevel;
565 enum note_visible_type visible_type;
566 unsigned int max_length;
573 struct note_type *type;
574 char setter[NICKSERV_HANDLE_LEN+1];
578 static unsigned int registered_channels;
579 static unsigned int banCount;
581 static const struct {
584 unsigned short level;
587 { "peon", "Peon", UL_PEON, '+' },
588 { "op", "Op", UL_OP, '@' },
589 { "master", "Master", UL_MASTER, '%' },
590 { "coowner", "Coowner", UL_COOWNER, '*' },
591 { "owner", "Owner", UL_OWNER, '!' },
592 { "helper", "BUG:", UL_HELPER, 'X' }
595 static const struct {
598 unsigned short default_value;
599 unsigned int old_idx;
600 unsigned int old_flag;
601 unsigned short flag_value;
603 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
604 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
605 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
606 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
607 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
608 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
609 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
610 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
611 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
612 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
613 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
616 struct charOptionValues {
619 } protectValues[] = {
620 { 'a', "CSMSG_PROTECT_ALL" },
621 { 'e', "CSMSG_PROTECT_EQUAL" },
622 { 'l', "CSMSG_PROTECT_LOWER" },
623 { 'n', "CSMSG_PROTECT_NONE" }
625 { 'd', "CSMSG_TOYS_DISABLED" },
626 { 'n', "CSMSG_TOYS_PRIVATE" },
627 { 'p', "CSMSG_TOYS_PUBLIC" }
628 }, topicRefreshValues[] = {
629 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
630 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
631 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
632 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
633 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
634 }, ctcpReactionValues[] = {
635 { 'k', "CSMSG_CTCPREACTION_KICK" },
636 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
637 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
638 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
641 static const struct {
645 unsigned int old_idx;
647 struct charOptionValues *values;
649 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
650 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
651 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
652 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
655 struct userData *helperList;
656 struct chanData *channelList;
657 static struct module *chanserv_module;
658 static unsigned int userCount;
660 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
661 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
664 user_level_from_name(const char *name, unsigned short clamp_level)
666 unsigned int level = 0, ii;
668 level = strtoul(name, NULL, 10);
669 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
670 if(!irccasecmp(name, accessLevels[ii].name))
671 level = accessLevels[ii].level;
672 if(level > clamp_level)
678 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
681 *minl = strtoul(arg, &sep, 10);
689 *maxl = strtoul(sep+1, &sep, 10);
697 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
699 struct userData *uData, **head;
701 if(!channel || !handle)
704 if(override && HANDLE_FLAGGED(handle, HELPING)
705 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
707 for(uData = helperList;
708 uData && uData->handle != handle;
709 uData = uData->next);
713 uData = calloc(1, sizeof(struct userData));
714 uData->handle = handle;
716 uData->access = UL_HELPER;
722 uData->next = helperList;
724 helperList->prev = uData;
732 for(uData = channel->users; uData; uData = uData->next)
733 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
736 head = &(channel->users);
739 if(uData && (uData != *head))
741 /* Shuffle the user to the head of whatever list he was in. */
743 uData->next->prev = uData->prev;
745 uData->prev->next = uData->next;
751 (**head).prev = uData;
758 /* Returns non-zero if user has at least the minimum access.
759 * exempt_owner is set when handling !set, so the owner can set things
762 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
764 struct userData *uData;
765 struct chanData *cData = channel->channel_info;
766 unsigned short minimum = cData->lvlOpts[opt];
769 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
772 if(minimum <= uData->access)
774 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
779 /* Scan for other users authenticated to the same handle
780 still in the channel. If so, keep them listed as present.
782 user is optional, if not null, it skips checking that userNode
783 (for the handle_part function) */
785 scan_user_presence(struct userData *uData, struct userNode *user)
789 if(IsSuspended(uData->channel)
790 || IsUserSuspended(uData)
791 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
803 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
805 unsigned int eflags, argc;
807 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
809 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
810 if(!channel->channel_info
811 || IsSuspended(channel->channel_info)
813 || !ircncasecmp(text, "ACTION ", 7))
815 /* Figure out the minimum level needed to CTCP the channel */
816 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
818 /* We need to enforce against them; do so. */
820 argv[0] = (char*)text;
821 argv[1] = user->nick;
823 if(GetUserMode(channel, user))
824 eflags |= ACTION_KICK;
825 switch(channel->channel_info->chOpts[chCTCPReaction]) {
826 default: case 'k': /* just do the kick */ break;
828 eflags |= ACTION_BAN;
831 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
832 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
835 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
836 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
839 argv[argc++] = bad_ctcp_reason;
840 eject_user(chanserv, channel, argc, argv, NULL, eflags);
844 chanserv_create_note_type(const char *name)
846 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
847 strcpy(ntype->name, name);
849 dict_insert(note_types, ntype->name, ntype);
854 chanserv_deref_note_type(void *data)
856 struct note_type *ntype = data;
858 if(--ntype->refs > 0)
864 chanserv_flush_note_type(struct note_type *ntype)
866 struct chanData *cData;
867 for(cData = channelList; cData; cData = cData->next)
868 dict_remove(cData->notes, ntype->name);
872 chanserv_truncate_notes(struct note_type *ntype)
874 struct chanData *cData;
876 unsigned int size = sizeof(*note) + ntype->max_length;
878 for(cData = channelList; cData; cData = cData->next) {
879 note = dict_find(cData->notes, ntype->name, NULL);
882 if(strlen(note->note) <= ntype->max_length)
884 dict_remove2(cData->notes, ntype->name, 1);
885 note = realloc(note, size);
886 note->note[ntype->max_length] = 0;
887 dict_insert(cData->notes, ntype->name, note);
891 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
894 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
897 unsigned int len = strlen(text);
899 if(len > type->max_length) len = type->max_length;
900 note = calloc(1, sizeof(*note) + len);
902 strncpy(note->setter, setter, sizeof(note->setter)-1);
903 memcpy(note->note, text, len);
905 dict_insert(channel->notes, type->name, note);
911 chanserv_free_note(void *data)
913 struct note *note = data;
915 chanserv_deref_note_type(note->type);
916 assert(note->type->refs > 0); /* must use delnote to remove the type */
920 static MODCMD_FUNC(cmd_createnote) {
921 struct note_type *ntype;
922 unsigned int arg = 1, existed = 0, max_length;
924 if((ntype = dict_find(note_types, argv[1], NULL)))
927 ntype = chanserv_create_note_type(argv[arg]);
928 if(!irccasecmp(argv[++arg], "privileged"))
931 ntype->set_access_type = NOTE_SET_PRIVILEGED;
932 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
934 else if(!irccasecmp(argv[arg], "channel"))
936 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
939 reply("CSMSG_INVALID_ACCESS", argv[arg]);
942 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
943 ntype->set_access.min_ulevel = ulvl;
945 else if(!irccasecmp(argv[arg], "setter"))
947 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
951 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
955 if(!irccasecmp(argv[++arg], "privileged"))
956 ntype->visible_type = NOTE_VIS_PRIVILEGED;
957 else if(!irccasecmp(argv[arg], "channel_users"))
958 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
959 else if(!irccasecmp(argv[arg], "all"))
960 ntype->visible_type = NOTE_VIS_ALL;
962 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
966 if((arg+1) >= argc) {
967 reply("MSG_MISSING_PARAMS", argv[0]);
970 max_length = strtoul(argv[++arg], NULL, 0);
971 if(max_length < 20 || max_length > 450)
973 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
976 if(existed && (max_length < ntype->max_length))
978 ntype->max_length = max_length;
979 chanserv_truncate_notes(ntype);
981 ntype->max_length = max_length;
984 reply("CSMSG_NOTE_MODIFIED", ntype->name);
986 reply("CSMSG_NOTE_CREATED", ntype->name);
991 dict_remove(note_types, ntype->name);
995 static MODCMD_FUNC(cmd_removenote) {
996 struct note_type *ntype;
999 ntype = dict_find(note_types, argv[1], NULL);
1000 force = (argc > 2) && !irccasecmp(argv[2], "force");
1003 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1010 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1013 chanserv_flush_note_type(ntype);
1015 dict_remove(note_types, argv[1]);
1016 reply("CSMSG_NOTE_DELETED", argv[1]);
1021 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1025 if(orig->modes_set & change->modes_clear)
1027 if(orig->modes_clear & change->modes_set)
1029 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1030 && strcmp(orig->new_key, change->new_key))
1032 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1033 && (orig->new_limit != change->new_limit))
1038 static char max_length_text[MAXLEN+1][16];
1040 static struct helpfile_expansion
1041 chanserv_expand_variable(const char *variable)
1043 struct helpfile_expansion exp;
1045 if(!irccasecmp(variable, "notes"))
1048 exp.type = HF_TABLE;
1049 exp.value.table.length = 1;
1050 exp.value.table.width = 3;
1051 exp.value.table.flags = 0;
1052 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1053 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1054 exp.value.table.contents[0][0] = "Note Type";
1055 exp.value.table.contents[0][1] = "Visibility";
1056 exp.value.table.contents[0][2] = "Max Length";
1057 for(it=dict_first(note_types); it; it=iter_next(it))
1059 struct note_type *ntype = iter_data(it);
1062 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1063 row = exp.value.table.length++;
1064 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1065 exp.value.table.contents[row][0] = ntype->name;
1066 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1067 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1069 if(!max_length_text[ntype->max_length][0])
1070 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1071 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1076 exp.type = HF_STRING;
1077 exp.value.str = NULL;
1081 static struct chanData*
1082 register_channel(struct chanNode *cNode, char *registrar)
1084 struct chanData *channel;
1085 enum levelOption lvlOpt;
1086 enum charOption chOpt;
1088 channel = calloc(1, sizeof(struct chanData));
1090 channel->notes = dict_new();
1091 dict_set_free_data(channel->notes, chanserv_free_note);
1093 channel->registrar = strdup(registrar);
1094 channel->registered = now;
1095 channel->visited = now;
1096 channel->limitAdjusted = now;
1097 channel->ownerTransfer = now;
1098 channel->flags = CHANNEL_DEFAULT_FLAGS;
1099 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1100 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1101 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1102 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1104 channel->prev = NULL;
1105 channel->next = channelList;
1108 channelList->prev = channel;
1109 channelList = channel;
1110 registered_channels++;
1112 channel->channel = cNode;
1114 cNode->channel_info = channel;
1119 static struct userData*
1120 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1122 struct userData *ud;
1124 if(access_level > UL_OWNER)
1127 ud = calloc(1, sizeof(*ud));
1128 ud->channel = channel;
1129 ud->handle = handle;
1131 ud->access = access_level;
1132 ud->info = info ? strdup(info) : NULL;
1135 ud->next = channel->users;
1137 channel->users->prev = ud;
1138 channel->users = ud;
1140 channel->userCount++;
1144 ud->u_next = ud->handle->channels;
1146 ud->u_next->u_prev = ud;
1147 ud->handle->channels = ud;
1152 static void unregister_channel(struct chanData *channel, const char *reason);
1155 del_channel_user(struct userData *user, int do_gc)
1157 struct chanData *channel = user->channel;
1159 channel->userCount--;
1163 user->prev->next = user->next;
1165 channel->users = user->next;
1167 user->next->prev = user->prev;
1170 user->u_prev->u_next = user->u_next;
1172 user->handle->channels = user->u_next;
1174 user->u_next->u_prev = user->u_prev;
1178 if(do_gc && !channel->users && !IsProtected(channel)) {
1179 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1180 unregister_channel(channel, "lost all users.");
1184 static void expire_ban(void *data);
1187 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1190 unsigned int ii, l1, l2;
1195 bd = malloc(sizeof(struct banData));
1197 bd->channel = channel;
1199 bd->triggered = triggered;
1200 bd->expires = expires;
1202 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1204 extern const char *hidden_host_suffix;
1205 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1209 l2 = strlen(old_name);
1212 if(irccasecmp(mask + l1 - l2, old_name))
1214 new_mask = alloca(MAXLEN);
1215 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1218 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1220 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1221 bd->reason = strdup(reason);
1224 timeq_add(expires, expire_ban, bd);
1227 bd->next = channel->bans;
1229 channel->bans->prev = bd;
1231 channel->banCount++;
1238 del_channel_ban(struct banData *ban)
1240 ban->channel->banCount--;
1244 ban->prev->next = ban->next;
1246 ban->channel->bans = ban->next;
1249 ban->next->prev = ban->prev;
1252 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1261 expire_ban(void *data)
1263 struct banData *bd = data;
1264 if(!IsSuspended(bd->channel))
1266 struct banList bans;
1267 struct mod_chanmode change;
1269 bans = bd->channel->channel->banlist;
1270 mod_chanmode_init(&change);
1271 for(ii=0; ii<bans.used; ii++)
1273 if(!strcmp(bans.list[ii]->ban, bd->mask))
1276 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1277 change.args[0].u.hostmask = bd->mask;
1278 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1284 del_channel_ban(bd);
1287 static void chanserv_expire_suspension(void *data);
1290 unregister_channel(struct chanData *channel, const char *reason)
1292 struct mod_chanmode change;
1293 char msgbuf[MAXLEN];
1295 /* After channel unregistration, the following must be cleaned
1297 - Channel information.
1300 - Channel suspension data.
1301 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1307 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1311 mod_chanmode_init(&change);
1312 change.modes_clear |= MODE_REGISTERED;
1313 mod_chanmode_announce(chanserv, channel->channel, &change);
1316 while(channel->users)
1317 del_channel_user(channel->users, 0);
1319 while(channel->bans)
1320 del_channel_ban(channel->bans);
1322 free(channel->topic);
1323 free(channel->registrar);
1324 free(channel->greeting);
1325 free(channel->user_greeting);
1326 free(channel->topic_mask);
1329 channel->prev->next = channel->next;
1331 channelList = channel->next;
1334 channel->next->prev = channel->prev;
1336 if(channel->suspended)
1338 struct chanNode *cNode = channel->channel;
1339 struct suspended *suspended, *next_suspended;
1341 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1343 next_suspended = suspended->previous;
1344 free(suspended->suspender);
1345 free(suspended->reason);
1346 if(suspended->expires)
1347 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1352 cNode->channel_info = NULL;
1354 channel->channel->channel_info = NULL;
1356 dict_delete(channel->notes);
1357 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1358 if(!IsSuspended(channel))
1359 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1360 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1361 UnlockChannel(channel->channel);
1363 registered_channels--;
1367 expire_channels(UNUSED_ARG(void *data))
1369 struct chanData *channel, *next;
1370 struct userData *user;
1371 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1373 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1374 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1376 for(channel = channelList; channel; channel = next)
1378 next = channel->next;
1380 /* See if the channel can be expired. */
1381 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1382 || IsProtected(channel))
1385 /* Make sure there are no high-ranking users still in the channel. */
1386 for(user=channel->users; user; user=user->next)
1387 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1392 /* Unregister the channel */
1393 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1394 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1395 unregister_channel(channel, "registration expired.");
1398 if(chanserv_conf.channel_expire_frequency)
1399 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1403 expire_dnrs(UNUSED_ARG(void *data))
1405 dict_iterator_t it, next;
1406 struct do_not_register *dnr;
1408 for(it = dict_first(handle_dnrs); it; it = next)
1410 dnr = iter_data(it);
1411 next = iter_next(it);
1412 if(dnr->expires && dnr->expires <= now)
1413 dict_remove(handle_dnrs, dnr->chan_name + 1);
1415 for(it = dict_first(plain_dnrs); it; it = next)
1417 dnr = iter_data(it);
1418 next = iter_next(it);
1419 if(dnr->expires && dnr->expires <= now)
1420 dict_remove(plain_dnrs, dnr->chan_name + 1);
1422 for(it = dict_first(mask_dnrs); it; it = next)
1424 dnr = iter_data(it);
1425 next = iter_next(it);
1426 if(dnr->expires && dnr->expires <= now)
1427 dict_remove(mask_dnrs, dnr->chan_name + 1);
1430 if(chanserv_conf.dnr_expire_frequency)
1431 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1435 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1437 char protect = channel->chOpts[chProtect];
1438 struct userData *cs_victim, *cs_aggressor;
1440 /* Don't protect if no one is to be protected, someone is attacking
1441 himself, or if the aggressor is an IRC Operator. */
1442 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1445 /* Don't protect if the victim isn't authenticated (because they
1446 can't be a channel user), unless we are to protect non-users
1448 cs_victim = GetChannelAccess(channel, victim->handle_info);
1449 if(protect != 'a' && !cs_victim)
1452 /* Protect if the aggressor isn't a user because at this point,
1453 the aggressor can only be less than or equal to the victim. */
1454 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1458 /* If the aggressor was a user, then the victim can't be helped. */
1465 if(cs_victim->access > cs_aggressor->access)
1470 if(cs_victim->access >= cs_aggressor->access)
1479 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1481 struct chanData *cData = channel->channel_info;
1482 struct userData *cs_victim;
1484 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1485 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1486 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1488 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1496 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1498 if(IsService(victim))
1500 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1504 if(protect_user(victim, user, channel->channel_info))
1506 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1513 static struct do_not_register *
1514 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1516 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1517 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1518 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1519 strcpy(dnr->reason, reason);
1521 dnr->expires = expires;
1522 if(dnr->chan_name[0] == '*')
1523 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1524 else if(strpbrk(dnr->chan_name, "*?"))
1525 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1527 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1531 static struct dnrList
1532 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1534 struct dnrList list;
1535 dict_iterator_t it, next;
1536 struct do_not_register *dnr;
1538 dnrList_init(&list);
1540 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1542 if(dnr->expires && dnr->expires <= now)
1543 dict_remove(handle_dnrs, handle);
1544 else if(list.used < max)
1545 dnrList_append(&list, dnr);
1548 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1550 if(dnr->expires && dnr->expires <= now)
1551 dict_remove(plain_dnrs, chan_name);
1552 else if(list.used < max)
1553 dnrList_append(&list, dnr);
1558 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1560 next = iter_next(it);
1561 if(!match_ircglob(chan_name, iter_key(it)))
1563 dnr = iter_data(it);
1564 if(dnr->expires && dnr->expires <= now)
1565 dict_remove(mask_dnrs, iter_key(it));
1567 dnrList_append(&list, dnr);
1574 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1576 struct userNode *user;
1577 char buf1[INTERVALLEN];
1578 char buf2[INTERVALLEN];
1585 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1590 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1591 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1595 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1598 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1603 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1605 struct dnrList list;
1608 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1609 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1610 dnr_print_func(list.list[ii], user);
1612 reply("CSMSG_MORE_DNRS", list.used - ii);
1617 struct do_not_register *
1618 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1620 struct dnrList list;
1621 struct do_not_register *dnr;
1623 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1624 dnr = list.used ? list.list[0] : NULL;
1629 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1631 struct do_not_register *dnr;
1632 dict_iterator_t it, next;
1633 unsigned int matches = 0;
1635 for(it = dict_first(dict); it; it = next)
1637 dnr = iter_data(it);
1638 next = iter_next(it);
1639 if(dnr->expires && dnr->expires <= now)
1641 dict_remove(dict, iter_key(it));
1644 dnr_print_func(dnr, user);
1651 static CHANSERV_FUNC(cmd_noregister)
1655 unsigned long expiry, duration;
1656 unsigned int matches;
1660 reply("CSMSG_DNR_SEARCH_RESULTS");
1661 matches = send_dnrs(user, handle_dnrs);
1662 matches += send_dnrs(user, plain_dnrs);
1663 matches += send_dnrs(user, mask_dnrs);
1665 reply("MSG_MATCH_COUNT", matches);
1667 reply("MSG_NO_MATCHES");
1673 if(!IsChannelName(target) && (*target != '*'))
1675 reply("CSMSG_NOT_DNR", target);
1683 reply("MSG_INVALID_DURATION", argv[2]);
1687 if(!strcmp(argv[2], "0"))
1689 else if((duration = ParseInterval(argv[2])))
1690 expiry = now + duration;
1693 reply("MSG_INVALID_DURATION", argv[2]);
1697 reason = unsplit_string(argv + 3, argc - 3, NULL);
1698 if((*target == '*') && !get_handle_info(target + 1))
1700 reply("MSG_HANDLE_UNKNOWN", target + 1);
1703 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1704 reply("CSMSG_NOREGISTER_CHANNEL", target);
1708 reply("CSMSG_DNR_SEARCH_RESULTS");
1710 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1712 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1714 reply("MSG_NO_MATCHES");
1718 static CHANSERV_FUNC(cmd_allowregister)
1720 const char *chan_name = argv[1];
1722 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1723 || dict_remove(plain_dnrs, chan_name)
1724 || dict_remove(mask_dnrs, chan_name))
1726 reply("CSMSG_DNR_REMOVED", chan_name);
1729 reply("CSMSG_NO_SUCH_DNR", chan_name);
1734 struct userNode *source;
1738 unsigned long min_set, max_set;
1739 unsigned long min_expires, max_expires;
1744 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1746 return !((dnr->set < search->min_set)
1747 || (dnr->set > search->max_set)
1748 || (dnr->expires < search->min_expires)
1749 || (search->max_expires
1750 && ((dnr->expires == 0)
1751 || (dnr->expires > search->max_expires)))
1752 || (search->chan_mask
1753 && !match_ircglob(dnr->chan_name, search->chan_mask))
1754 || (search->setter_mask
1755 && !match_ircglob(dnr->setter, search->setter_mask))
1756 || (search->reason_mask
1757 && !match_ircglob(dnr->reason, search->reason_mask)));
1760 static struct dnr_search *
1761 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1763 struct dnr_search *discrim;
1766 discrim = calloc(1, sizeof(*discrim));
1767 discrim->source = user;
1768 discrim->chan_mask = NULL;
1769 discrim->setter_mask = NULL;
1770 discrim->reason_mask = NULL;
1771 discrim->max_set = INT_MAX;
1772 discrim->limit = 50;
1774 for(ii=0; ii<argc; ++ii)
1778 reply("MSG_MISSING_PARAMS", argv[ii]);
1781 else if(0 == irccasecmp(argv[ii], "channel"))
1783 discrim->chan_mask = argv[++ii];
1785 else if(0 == irccasecmp(argv[ii], "setter"))
1787 discrim->setter_mask = argv[++ii];
1789 else if(0 == irccasecmp(argv[ii], "reason"))
1791 discrim->reason_mask = argv[++ii];
1793 else if(0 == irccasecmp(argv[ii], "limit"))
1795 discrim->limit = strtoul(argv[++ii], NULL, 0);
1797 else if(0 == irccasecmp(argv[ii], "set"))
1799 const char *cmp = argv[++ii];
1802 discrim->min_set = now - ParseInterval(cmp + 2);
1804 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1805 } else if(cmp[0] == '=') {
1806 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1807 } else if(cmp[0] == '>') {
1809 discrim->max_set = now - ParseInterval(cmp + 2);
1811 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1813 discrim->max_set = now - (ParseInterval(cmp) - 1);
1816 else if(0 == irccasecmp(argv[ii], "expires"))
1818 const char *cmp = argv[++ii];
1821 discrim->max_expires = now + ParseInterval(cmp + 2);
1823 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1824 } else if(cmp[0] == '=') {
1825 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1826 } else if(cmp[0] == '>') {
1828 discrim->min_expires = now + ParseInterval(cmp + 2);
1830 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1832 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1837 reply("MSG_INVALID_CRITERIA", argv[ii]);
1848 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1851 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1853 struct do_not_register *dnr;
1854 dict_iterator_t next;
1859 /* Initialize local variables. */
1862 if(discrim->chan_mask)
1864 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1865 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1869 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1871 /* Check against account-based DNRs. */
1872 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1873 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1876 else if(target_fixed)
1878 /* Check against channel-based DNRs. */
1879 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1880 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1885 /* Exhaustively search account DNRs. */
1886 for(it = dict_first(handle_dnrs); it; it = next)
1888 next = iter_next(it);
1889 dnr = iter_data(it);
1890 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1894 /* Do the same for channel DNRs. */
1895 for(it = dict_first(plain_dnrs); it; it = next)
1897 next = iter_next(it);
1898 dnr = iter_data(it);
1899 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1903 /* Do the same for wildcarded channel DNRs. */
1904 for(it = dict_first(mask_dnrs); it; it = next)
1906 next = iter_next(it);
1907 dnr = iter_data(it);
1908 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1916 dnr_remove_func(struct do_not_register *match, void *extra)
1918 struct userNode *user;
1921 chan_name = alloca(strlen(match->chan_name) + 1);
1922 strcpy(chan_name, match->chan_name);
1924 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1925 || dict_remove(plain_dnrs, chan_name)
1926 || dict_remove(mask_dnrs, chan_name))
1928 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1934 dnr_count_func(struct do_not_register *match, void *extra)
1936 return 0; (void)match; (void)extra;
1939 static MODCMD_FUNC(cmd_dnrsearch)
1941 struct dnr_search *discrim;
1942 dnr_search_func action;
1943 struct svccmd *subcmd;
1944 unsigned int matches;
1947 sprintf(buf, "dnrsearch %s", argv[1]);
1948 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1951 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1954 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1956 if(!irccasecmp(argv[1], "print"))
1957 action = dnr_print_func;
1958 else if(!irccasecmp(argv[1], "remove"))
1959 action = dnr_remove_func;
1960 else if(!irccasecmp(argv[1], "count"))
1961 action = dnr_count_func;
1964 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1968 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1972 if(action == dnr_print_func)
1973 reply("CSMSG_DNR_SEARCH_RESULTS");
1974 matches = dnr_search(discrim, action, user);
1976 reply("MSG_MATCH_COUNT", matches);
1978 reply("MSG_NO_MATCHES");
1984 chanserv_get_owned_count(struct handle_info *hi)
1986 struct userData *cList;
1989 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1990 if(cList->access == UL_OWNER)
1995 static CHANSERV_FUNC(cmd_register)
1997 struct handle_info *handle;
1998 struct chanData *cData;
1999 struct modeNode *mn;
2000 char reason[MAXLEN];
2002 unsigned int new_channel, force=0;
2003 struct do_not_register *dnr;
2007 if(channel->channel_info)
2009 reply("CSMSG_ALREADY_REGGED", channel->name);
2013 if(channel->bad_channel)
2015 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2020 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2022 reply("CSMSG_MUST_BE_OPPED", channel->name);
2027 chan_name = channel->name;
2031 if((argc < 2) || !IsChannelName(argv[1]))
2033 reply("MSG_NOT_CHANNEL_NAME");
2037 if(opserv_bad_channel(argv[1]))
2039 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2044 chan_name = argv[1];
2047 if(argc >= (new_channel+2))
2049 if(!IsHelping(user))
2051 reply("CSMSG_PROXY_FORBIDDEN");
2055 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2057 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2058 dnr = chanserv_is_dnr(chan_name, handle);
2062 handle = user->handle_info;
2063 dnr = chanserv_is_dnr(chan_name, handle);
2067 if(!IsHelping(user))
2068 reply("CSMSG_DNR_CHANNEL", chan_name);
2070 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2074 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2076 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2081 channel = AddChannel(argv[1], now, NULL, NULL);
2083 cData = register_channel(channel, user->handle_info->handle);
2084 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2085 cData->modes = chanserv_conf.default_modes;
2087 cData->modes.modes_set |= MODE_REGISTERED;
2088 if (IsOffChannel(cData))
2090 mod_chanmode_announce(chanserv, channel, &cData->modes);
2094 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2095 change->args[change->argc].mode = MODE_CHANOP;
2096 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2098 mod_chanmode_announce(chanserv, channel, change);
2099 mod_chanmode_free(change);
2102 /* Initialize the channel's max user record. */
2103 cData->max = channel->members.used;
2105 if(handle != user->handle_info)
2106 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2108 reply("CSMSG_REG_SUCCESS", channel->name);
2110 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2111 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2116 make_confirmation_string(struct userData *uData)
2118 static char strbuf[16];
2123 for(src = uData->handle->handle; *src; )
2124 accum = accum * 31 + toupper(*src++);
2126 for(src = uData->channel->channel->name; *src; )
2127 accum = accum * 31 + toupper(*src++);
2128 sprintf(strbuf, "%08x", accum);
2132 static CHANSERV_FUNC(cmd_unregister)
2135 char reason[MAXLEN];
2136 struct chanData *cData;
2137 struct userData *uData;
2139 cData = channel->channel_info;
2142 reply("CSMSG_NOT_REGISTERED", channel->name);
2146 uData = GetChannelUser(cData, user->handle_info);
2147 if(!uData || (uData->access < UL_OWNER))
2149 reply("CSMSG_NO_ACCESS");
2153 if(IsProtected(cData))
2155 reply("CSMSG_UNREG_NODELETE", channel->name);
2159 if(!IsHelping(user))
2161 const char *confirm_string;
2162 if(IsSuspended(cData))
2164 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2167 confirm_string = make_confirmation_string(uData);
2168 if((argc < 2) || strcmp(argv[1], confirm_string))
2170 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2175 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2176 name = strdup(channel->name);
2177 unregister_channel(cData, reason);
2178 spamserv_cs_unregister(user, channel, manually, "unregistered");
2179 reply("CSMSG_UNREG_SUCCESS", name);
2185 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2187 extern struct userNode *spamserv;
2188 struct mod_chanmode *change;
2190 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2192 change = mod_chanmode_alloc(2);
2194 change->args[0].mode = MODE_CHANOP;
2195 change->args[0].u.member = AddChannelUser(chanserv, channel);
2196 change->args[1].mode = MODE_CHANOP;
2197 change->args[1].u.member = AddChannelUser(spamserv, channel);
2201 change = mod_chanmode_alloc(1);
2203 change->args[0].mode = MODE_CHANOP;
2204 change->args[0].u.member = AddChannelUser(chanserv, channel);
2207 mod_chanmode_announce(chanserv, channel, change);
2208 mod_chanmode_free(change);
2211 static CHANSERV_FUNC(cmd_move)
2213 struct mod_chanmode change;
2214 struct chanNode *target;
2215 struct modeNode *mn;
2216 struct userData *uData;
2217 char reason[MAXLEN];
2218 struct do_not_register *dnr;
2219 int chanserv_join = 0, spamserv_join;
2223 if(IsProtected(channel->channel_info))
2225 reply("CSMSG_MOVE_NODELETE", channel->name);
2229 if(!IsChannelName(argv[1]))
2231 reply("MSG_NOT_CHANNEL_NAME");
2235 if(opserv_bad_channel(argv[1]))
2237 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2241 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2243 for(uData = channel->channel_info->users; uData; uData = uData->next)
2245 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2247 if(!IsHelping(user))
2248 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2250 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2256 mod_chanmode_init(&change);
2257 if(!(target = GetChannel(argv[1])))
2259 target = AddChannel(argv[1], now, NULL, NULL);
2260 if(!IsSuspended(channel->channel_info))
2263 else if(target->channel_info)
2265 reply("CSMSG_ALREADY_REGGED", target->name);
2268 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2269 && !IsHelping(user))
2271 reply("CSMSG_MUST_BE_OPPED", target->name);
2274 else if(!IsSuspended(channel->channel_info))
2279 /* Clear MODE_REGISTERED from old channel, add it to new. */
2281 change.modes_clear = MODE_REGISTERED;
2282 mod_chanmode_announce(chanserv, channel, &change);
2283 change.modes_clear = 0;
2284 change.modes_set = MODE_REGISTERED;
2285 mod_chanmode_announce(chanserv, target, &change);
2288 /* Move the channel_info to the target channel; it
2289 shouldn't be necessary to clear timeq callbacks
2290 for the old channel. */
2291 target->channel_info = channel->channel_info;
2292 target->channel_info->channel = target;
2293 channel->channel_info = NULL;
2295 /* Check whether users are present in the new channel. */
2296 for(uData = target->channel_info->users; uData; uData = uData->next)
2297 scan_user_presence(uData, NULL);
2299 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2302 ss_cs_join_channel(target, spamserv_join);
2304 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2305 if(!IsSuspended(target->channel_info))
2307 char reason2[MAXLEN];
2308 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2309 DelChannelUser(chanserv, channel, reason2, 0);
2311 UnlockChannel(channel);
2312 LockChannel(target);
2313 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2314 reply("CSMSG_MOVE_SUCCESS", target->name);
2319 merge_users(struct chanData *source, struct chanData *target)
2321 struct userData *suData, *tuData, *next;
2327 /* Insert the source's users into the scratch area. */
2328 for(suData = source->users; suData; suData = suData->next)
2329 dict_insert(merge, suData->handle->handle, suData);
2331 /* Iterate through the target's users, looking for
2332 users common to both channels. The lower access is
2333 removed from either the scratch area or target user
2335 for(tuData = target->users; tuData; tuData = next)
2337 struct userData *choice;
2339 next = tuData->next;
2341 /* If a source user exists with the same handle as a target
2342 channel's user, resolve the conflict by removing one. */
2343 suData = dict_find(merge, tuData->handle->handle, NULL);
2347 /* Pick the data we want to keep. */
2348 /* If the access is the same, use the later seen time. */
2349 if(suData->access == tuData->access)
2350 choice = (suData->seen > tuData->seen) ? suData : tuData;
2351 else /* Otherwise, keep the higher access level. */
2352 choice = (suData->access > tuData->access) ? suData : tuData;
2353 /* Use the later seen time. */
2354 if(suData->seen < tuData->seen)
2355 suData->seen = tuData->seen;
2357 tuData->seen = suData->seen;
2359 /* Remove the user that wasn't picked. */
2360 if(choice == tuData)
2362 dict_remove(merge, suData->handle->handle);
2363 del_channel_user(suData, 0);
2366 del_channel_user(tuData, 0);
2369 /* Move the remaining users to the target channel. */
2370 for(it = dict_first(merge); it; it = iter_next(it))
2372 suData = iter_data(it);
2374 /* Insert the user into the target channel's linked list. */
2375 suData->prev = NULL;
2376 suData->next = target->users;
2377 suData->channel = target;
2380 target->users->prev = suData;
2381 target->users = suData;
2383 /* Update the user counts for the target channel; the
2384 source counts are left alone. */
2385 target->userCount++;
2387 /* Check whether the user is in the target channel. */
2388 scan_user_presence(suData, NULL);
2391 /* Possible to assert (source->users == NULL) here. */
2392 source->users = NULL;
2397 merge_bans(struct chanData *source, struct chanData *target)
2399 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2401 /* Hold on to the original head of the target ban list
2402 to avoid comparing source bans with source bans. */
2403 tFront = target->bans;
2405 /* Perform a totally expensive O(n*m) merge, ick. */
2406 for(sbData = source->bans; sbData; sbData = sNext)
2408 /* Flag to track whether the ban's been moved
2409 to the destination yet. */
2412 /* Possible to assert (sbData->prev == NULL) here. */
2413 sNext = sbData->next;
2415 for(tbData = tFront; tbData; tbData = tNext)
2417 tNext = tbData->next;
2419 /* Perform two comparisons between each source
2420 and target ban, conflicts are resolved by
2421 keeping the broader ban and copying the later
2422 expiration and triggered time. */
2423 if(match_ircglobs(tbData->mask, sbData->mask))
2425 /* There is a broader ban in the target channel that
2426 overrides one in the source channel; remove the
2427 source ban and break. */
2428 if(sbData->expires > tbData->expires)
2429 tbData->expires = sbData->expires;
2430 if(sbData->triggered > tbData->triggered)
2431 tbData->triggered = sbData->triggered;
2432 del_channel_ban(sbData);
2435 else if(match_ircglobs(sbData->mask, tbData->mask))
2437 /* There is a broader ban in the source channel that
2438 overrides one in the target channel; remove the
2439 target ban, fall through and move the source over. */
2440 if(tbData->expires > sbData->expires)
2441 sbData->expires = tbData->expires;
2442 if(tbData->triggered > sbData->triggered)
2443 sbData->triggered = tbData->triggered;
2444 if(tbData == tFront)
2446 del_channel_ban(tbData);
2449 /* Source bans can override multiple target bans, so
2450 we allow a source to run through this loop multiple
2451 times, but we can only move it once. */
2456 /* Remove the source ban from the source ban list. */
2458 sbData->next->prev = sbData->prev;
2460 /* Modify the source ban's associated channel. */
2461 sbData->channel = target;
2463 /* Insert the ban into the target channel's linked list. */
2464 sbData->prev = NULL;
2465 sbData->next = target->bans;
2468 target->bans->prev = sbData;
2469 target->bans = sbData;
2471 /* Update the user counts for the target channel. */
2476 /* Possible to assert (source->bans == NULL) here. */
2477 source->bans = NULL;
2481 merge_data(struct chanData *source, struct chanData *target)
2483 /* Use more recent visited and owner-transfer time; use older
2484 * registered time. Bitwise or may_opchan. Use higher max.
2485 * Do not touch last_refresh, ban count or user counts.
2487 if(source->visited > target->visited)
2488 target->visited = source->visited;
2489 if(source->registered < target->registered)
2490 target->registered = source->registered;
2491 if(source->ownerTransfer > target->ownerTransfer)
2492 target->ownerTransfer = source->ownerTransfer;
2493 if(source->may_opchan)
2494 target->may_opchan = 1;
2495 if(source->max > target->max)
2496 target->max = source->max;
2500 merge_channel(struct chanData *source, struct chanData *target)
2502 merge_users(source, target);
2503 merge_bans(source, target);
2504 merge_data(source, target);
2507 static CHANSERV_FUNC(cmd_merge)
2509 struct userData *target_user;
2510 struct chanNode *target;
2511 char reason[MAXLEN];
2515 /* Make sure the target channel exists and is registered to the user
2516 performing the command. */
2517 if(!(target = GetChannel(argv[1])))
2519 reply("MSG_INVALID_CHANNEL");
2523 if(!target->channel_info)
2525 reply("CSMSG_NOT_REGISTERED", target->name);
2529 if(IsProtected(channel->channel_info))
2531 reply("CSMSG_MERGE_NODELETE");
2535 if(IsSuspended(target->channel_info))
2537 reply("CSMSG_MERGE_SUSPENDED");
2541 if(channel == target)
2543 reply("CSMSG_MERGE_SELF");
2547 target_user = GetChannelUser(target->channel_info, user->handle_info);
2548 if(!target_user || (target_user->access < UL_OWNER))
2550 reply("CSMSG_MERGE_NOT_OWNER");
2554 /* Merge the channel structures and associated data. */
2555 merge_channel(channel->channel_info, target->channel_info);
2556 spamserv_cs_move_merge(user, channel, target, 0);
2557 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2558 unregister_channel(channel->channel_info, reason);
2559 reply("CSMSG_MERGE_SUCCESS", target->name);
2563 static CHANSERV_FUNC(cmd_opchan)
2565 struct mod_chanmode change;
2566 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2568 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2571 channel->channel_info->may_opchan = 0;
2572 mod_chanmode_init(&change);
2574 change.args[0].mode = MODE_CHANOP;
2575 change.args[0].u.member = GetUserMode(channel, chanserv);
2576 if(!change.args[0].u.member)
2578 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2581 mod_chanmode_announce(chanserv, channel, &change);
2582 reply("CSMSG_OPCHAN_DONE", channel->name);
2586 static CHANSERV_FUNC(cmd_adduser)
2588 struct userData *actee;
2589 struct userData *actor, *real_actor;
2590 struct handle_info *handle;
2591 unsigned short access_level, override = 0;
2595 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2597 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2601 access_level = user_level_from_name(argv[2], UL_OWNER);
2604 reply("CSMSG_INVALID_ACCESS", argv[2]);
2608 actor = GetChannelUser(channel->channel_info, user->handle_info);
2609 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2611 if(actor->access <= access_level)
2613 reply("CSMSG_NO_BUMP_ACCESS");
2617 /* Trying to add someone with equal/more access? */
2618 if (!real_actor || real_actor->access <= access_level)
2619 override = CMD_LOG_OVERRIDE;
2621 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2624 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2626 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2630 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2631 scan_user_presence(actee, NULL);
2632 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2633 return 1 | override;
2636 static CHANSERV_FUNC(cmd_clvl)
2638 struct handle_info *handle;
2639 struct userData *victim;
2640 struct userData *actor, *real_actor;
2641 unsigned short new_access, override = 0;
2642 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2646 actor = GetChannelUser(channel->channel_info, user->handle_info);
2647 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2649 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2652 if(handle == user->handle_info && !privileged)
2654 reply("CSMSG_NO_SELF_CLVL");
2658 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2660 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2664 if(actor->access <= victim->access && !privileged)
2666 reply("MSG_USER_OUTRANKED", handle->handle);
2670 new_access = user_level_from_name(argv[2], UL_OWNER);
2674 reply("CSMSG_INVALID_ACCESS", argv[2]);
2678 if(new_access >= actor->access && !privileged)
2680 reply("CSMSG_NO_BUMP_ACCESS");
2684 /* Trying to clvl a equal/higher user? */
2685 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2686 override = CMD_LOG_OVERRIDE;
2687 /* Trying to clvl someone to equal/higher access? */
2688 if(!real_actor || new_access >= real_actor->access)
2689 override = CMD_LOG_OVERRIDE;
2690 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2691 * If they lower their own access it's not a big problem.
2694 victim->access = new_access;
2695 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2696 return 1 | override;
2699 static CHANSERV_FUNC(cmd_deluser)
2701 struct handle_info *handle;
2702 struct userData *victim;
2703 struct userData *actor, *real_actor;
2704 unsigned short access_level, override = 0;
2709 actor = GetChannelUser(channel->channel_info, user->handle_info);
2710 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2712 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2715 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2717 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2723 access_level = user_level_from_name(argv[1], UL_OWNER);
2726 reply("CSMSG_INVALID_ACCESS", argv[1]);
2729 if(access_level != victim->access)
2731 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2737 access_level = victim->access;
2740 if((actor->access <= victim->access) && !IsHelping(user))
2742 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2746 /* If people delete themselves it is an override, but they
2747 * could've used deleteme so we don't log it as an override
2749 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2750 override = CMD_LOG_OVERRIDE;
2752 chan_name = strdup(channel->name);
2753 del_channel_user(victim, 1);
2754 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2756 return 1 | override;
2760 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2762 struct userData *actor, *real_actor, *uData, *next;
2763 unsigned int override = 0;
2765 actor = GetChannelUser(channel->channel_info, user->handle_info);
2766 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2768 if(min_access > max_access)
2770 reply("CSMSG_BAD_RANGE", min_access, max_access);
2774 if(actor->access <= max_access)
2776 reply("CSMSG_NO_ACCESS");
2780 if(!real_actor || real_actor->access <= max_access)
2781 override = CMD_LOG_OVERRIDE;
2783 for(uData = channel->channel_info->users; uData; uData = next)
2787 if((uData->access >= min_access)
2788 && (uData->access <= max_access)
2789 && match_ircglob(uData->handle->handle, mask))
2790 del_channel_user(uData, 1);
2793 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2794 return 1 | override;
2797 static CHANSERV_FUNC(cmd_mdelowner)
2799 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2802 static CHANSERV_FUNC(cmd_mdelcoowner)
2804 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2807 static CHANSERV_FUNC(cmd_mdelmaster)
2809 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2812 static CHANSERV_FUNC(cmd_mdelop)
2814 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2817 static CHANSERV_FUNC(cmd_mdelpeon)
2819 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2823 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2825 struct banData *bData, *next;
2826 char interval[INTERVALLEN];
2828 unsigned long limit;
2831 limit = now - duration;
2832 for(bData = channel->channel_info->bans; bData; bData = next)
2836 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2839 del_channel_ban(bData);
2843 intervalString(interval, duration, user->handle_info);
2844 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2849 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2851 struct userData *actor, *uData, *next;
2852 char interval[INTERVALLEN];
2854 unsigned long limit;
2856 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2857 if(min_access > max_access)
2859 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2863 if(!actor || actor->access <= max_access)
2865 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2870 limit = now - duration;
2871 for(uData = channel->channel_info->users; uData; uData = next)
2875 if((uData->seen > limit)
2877 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2880 if(((uData->access >= min_access) && (uData->access <= max_access))
2881 || (!max_access && (uData->access < actor->access)))
2883 del_channel_user(uData, 1);
2891 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2893 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2897 static CHANSERV_FUNC(cmd_trim)
2899 unsigned long duration;
2900 unsigned short min_level, max_level;
2905 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2906 duration = ParseInterval(argv[2]);
2909 reply("CSMSG_CANNOT_TRIM");
2913 if(!irccasecmp(argv[1], "bans"))
2915 cmd_trim_bans(user, channel, duration);
2918 else if(!irccasecmp(argv[1], "users"))
2920 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2923 else if(parse_level_range(&min_level, &max_level, argv[1]))
2925 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2928 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2930 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2935 reply("CSMSG_INVALID_TRIM", argv[1]);
2940 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2941 to the user. cmd_all takes advantage of this. */
2942 static CHANSERV_FUNC(cmd_up)
2944 struct mod_chanmode change;
2945 struct userData *uData;
2948 mod_chanmode_init(&change);
2950 change.args[0].u.member = GetUserMode(channel, user);
2951 if(!change.args[0].u.member)
2954 reply("MSG_CHANNEL_ABSENT", channel->name);
2958 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2962 reply("CSMSG_GODMODE_UP", argv[0]);
2965 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2967 change.args[0].mode = MODE_CHANOP;
2968 errmsg = "CSMSG_ALREADY_OPPED";
2970 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2972 change.args[0].mode = MODE_VOICE;
2973 errmsg = "CSMSG_ALREADY_VOICED";
2978 reply("CSMSG_NO_ACCESS");
2981 change.args[0].mode &= ~change.args[0].u.member->modes;
2982 if(!change.args[0].mode)
2985 reply(errmsg, channel->name);
2988 modcmd_chanmode_announce(&change);
2992 static CHANSERV_FUNC(cmd_down)
2994 struct mod_chanmode change;
2996 mod_chanmode_init(&change);
2998 change.args[0].u.member = GetUserMode(channel, user);
2999 if(!change.args[0].u.member)
3002 reply("MSG_CHANNEL_ABSENT", channel->name);
3006 if(!change.args[0].u.member->modes)
3009 reply("CSMSG_ALREADY_DOWN", channel->name);
3013 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3014 modcmd_chanmode_announce(&change);
3018 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)
3020 struct userData *cList;
3022 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3024 if(IsSuspended(cList->channel)
3025 || IsUserSuspended(cList)
3026 || !GetUserMode(cList->channel->channel, user))
3029 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3035 static CHANSERV_FUNC(cmd_upall)
3037 return cmd_all(CSFUNC_ARGS, cmd_up);
3040 static CHANSERV_FUNC(cmd_downall)
3042 return cmd_all(CSFUNC_ARGS, cmd_down);
3045 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3046 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3049 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)
3051 unsigned int ii, valid;
3052 struct userNode *victim;
3053 struct mod_chanmode *change;
3055 change = mod_chanmode_alloc(argc - 1);
3057 for(ii=valid=0; ++ii < argc; )
3059 if(!(victim = GetUserH(argv[ii])))
3061 change->args[valid].mode = mode;
3062 change->args[valid].u.member = GetUserMode(channel, victim);
3063 if(!change->args[valid].u.member)
3065 if(validate && !validate(user, channel, victim))
3070 change->argc = valid;
3071 if(valid < (argc-1))
3072 reply("CSMSG_PROCESS_FAILED");
3075 modcmd_chanmode_announce(change);
3076 reply(action, channel->name);
3078 mod_chanmode_free(change);
3082 static CHANSERV_FUNC(cmd_op)
3084 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3087 static CHANSERV_FUNC(cmd_deop)
3089 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3092 static CHANSERV_FUNC(cmd_voice)
3094 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3097 static CHANSERV_FUNC(cmd_devoice)
3099 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3103 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3109 for(ii=0; ii<channel->members.used; ii++)
3111 struct modeNode *mn = channel->members.list[ii];
3113 if(IsService(mn->user))
3116 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3119 if(protect_user(mn->user, user, channel->channel_info))
3123 victims[(*victimCount)++] = mn;
3129 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3131 struct userNode *victim;
3132 struct modeNode **victims;
3133 unsigned int offset, n, victimCount, duration = 0;
3134 char *reason = "Bye.", *ban, *name;
3135 char interval[INTERVALLEN];
3137 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3138 REQUIRE_PARAMS(offset);
3141 reason = unsplit_string(argv + offset, argc - offset, NULL);
3142 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3144 /* Truncate the reason to a length of TOPICLEN, as
3145 the ircd does; however, leave room for an ellipsis
3146 and the kicker's nick. */
3147 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3151 if((victim = GetUserH(argv[1])))
3153 victims = alloca(sizeof(victims[0]));
3154 victims[0] = GetUserMode(channel, victim);
3155 /* XXX: The comparison with ACTION_KICK is just because all
3156 * other actions can work on users outside the channel, and we
3157 * want to allow those (e.g. unbans) in that case. If we add
3158 * some other ejection action for in-channel users, change
3160 victimCount = victims[0] ? 1 : 0;
3162 if(IsService(victim))
3164 reply("MSG_SERVICE_IMMUNE", victim->nick);
3168 if((action == ACTION_KICK) && !victimCount)
3170 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3174 if(protect_user(victim, user, channel->channel_info))
3176 reply("CSMSG_USER_PROTECTED", victim->nick);
3180 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3181 name = victim->nick;
3183 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3185 struct handle_info *hi;
3186 extern const char *titlehost_suffix;
3187 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3188 const char *accountname = argv[1] + 1;
3190 if(!(hi = get_handle_info(accountname)))
3192 reply("MSG_HANDLE_UNKNOWN", accountname);
3196 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3197 victims = alloca(sizeof(victims[0]) * channel->members.used);
3199 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3201 reply("CSMSG_MASK_PROTECTED", banmask);
3205 if((action == ACTION_KICK) && (victimCount == 0))
3207 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3211 name = ban = strdup(banmask);
3215 if(!is_ircmask(argv[1]))
3217 reply("MSG_NICK_UNKNOWN", argv[1]);
3221 victims = alloca(sizeof(victims[0]) * channel->members.used);
3223 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3225 reply("CSMSG_MASK_PROTECTED", argv[1]);
3229 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3231 reply("CSMSG_LAME_MASK", argv[1]);
3235 if((action == ACTION_KICK) && (victimCount == 0))
3237 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3241 name = ban = strdup(argv[1]);
3244 /* Truncate the ban in place if necessary; we must ensure
3245 that 'ban' is a valid ban mask before sanitizing it. */
3246 sanitize_ircmask(ban);
3248 if(action & ACTION_ADD_BAN)
3250 struct banData *bData, *next;
3252 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3254 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3259 if(action & ACTION_ADD_TIMED_BAN)
3261 duration = ParseInterval(argv[2]);
3265 reply("CSMSG_DURATION_TOO_LOW");
3269 else if(duration > (86400 * 365 * 2))
3271 reply("CSMSG_DURATION_TOO_HIGH");
3277 for(bData = channel->channel_info->bans; bData; bData = next)
3279 if(match_ircglobs(bData->mask, ban))
3281 int exact = !irccasecmp(bData->mask, ban);
3283 /* The ban is redundant; there is already a ban
3284 with the same effect in place. */
3288 free(bData->reason);
3289 bData->reason = strdup(reason);
3290 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3292 reply("CSMSG_REASON_CHANGE", ban);
3296 if(exact && bData->expires)
3300 /* If the ban matches an existing one exactly,
3301 extend the expiration time if the provided
3302 duration is longer. */
3303 if(duration && (now + duration > bData->expires))
3305 bData->expires = now + duration;
3316 /* Delete the expiration timeq entry and
3317 requeue if necessary. */
3318 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3321 timeq_add(bData->expires, expire_ban, bData);
3325 /* automated kickban */
3328 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3330 reply("CSMSG_BAN_ADDED", name, channel->name);
3336 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3343 if(match_ircglobs(ban, bData->mask))
3345 /* The ban we are adding makes previously existing
3346 bans redundant; silently remove them. */
3347 del_channel_ban(bData);
3351 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);
3353 name = ban = strdup(bData->mask);
3357 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3359 extern const char *hidden_host_suffix;
3360 const char *old_name = chanserv_conf.old_ban_names->list[n];
3362 unsigned int l1, l2;
3365 l2 = strlen(old_name);
3368 if(irccasecmp(ban + l1 - l2, old_name))
3370 new_mask = malloc(MAXLEN);
3371 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3373 name = ban = new_mask;
3378 if(action & ACTION_BAN)
3380 unsigned int exists;
3381 struct mod_chanmode *change;
3383 if(channel->banlist.used >= MAXBANS)
3386 reply("CSMSG_BANLIST_FULL", channel->name);
3391 exists = ChannelBanExists(channel, ban);
3392 change = mod_chanmode_alloc(victimCount + 1);
3393 for(n = 0; n < victimCount; ++n)
3395 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3396 change->args[n].u.member = victims[n];
3400 change->args[n].mode = MODE_BAN;
3401 change->args[n++].u.hostmask = ban;
3405 modcmd_chanmode_announce(change);
3407 mod_chanmode_announce(chanserv, channel, change);
3408 mod_chanmode_free(change);
3410 if(exists && (action == ACTION_BAN))
3413 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3419 if(action & ACTION_KICK)
3421 char kick_reason[MAXLEN];
3422 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3424 for(n = 0; n < victimCount; n++)
3425 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3430 /* No response, since it was automated. */
3432 else if(action & ACTION_ADD_BAN)
3435 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3437 reply("CSMSG_BAN_ADDED", name, channel->name);
3439 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3440 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3441 else if(action & ACTION_BAN)
3442 reply("CSMSG_BAN_DONE", name, channel->name);
3443 else if(action & ACTION_KICK && victimCount)
3444 reply("CSMSG_KICK_DONE", name, channel->name);
3450 static CHANSERV_FUNC(cmd_kickban)
3452 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3455 static CHANSERV_FUNC(cmd_kick)
3457 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3460 static CHANSERV_FUNC(cmd_ban)
3462 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3465 static CHANSERV_FUNC(cmd_addban)
3467 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3470 static CHANSERV_FUNC(cmd_addtimedban)
3472 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3475 static struct mod_chanmode *
3476 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3478 struct mod_chanmode *change;
3479 unsigned char *match;
3480 unsigned int ii, count;
3482 match = alloca(bans->used);
3485 for(ii = count = 0; ii < bans->used; ++ii)
3487 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3488 MATCH_USENICK | MATCH_VISIBLE);
3495 for(ii = count = 0; ii < bans->used; ++ii)
3497 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3504 change = mod_chanmode_alloc(count);
3505 for(ii = count = 0; ii < bans->used; ++ii)
3509 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3510 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3512 assert(count == change->argc);
3517 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3519 struct userNode *actee;
3525 /* may want to allow a comma delimited list of users... */
3526 if(!(actee = GetUserH(argv[1])))
3528 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3530 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3531 const char *accountname = argv[1] + 1;
3533 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3534 mask = strdup(banmask);
3536 else if(!is_ircmask(argv[1]))
3538 reply("MSG_NICK_UNKNOWN", argv[1]);
3543 mask = strdup(argv[1]);
3547 /* We don't sanitize the mask here because ircu
3549 if(action & ACTION_UNBAN)
3551 struct mod_chanmode *change;
3552 change = find_matching_bans(&channel->banlist, actee, mask);
3557 modcmd_chanmode_announce(change);
3558 for(ii = 0; ii < change->argc; ++ii)
3559 free((char*)change->args[ii].u.hostmask);
3560 mod_chanmode_free(change);
3565 if(action & ACTION_DEL_BAN)
3567 struct banData *ban, *next;
3569 ban = channel->channel_info->bans;
3573 for( ; ban && !user_matches_glob(actee, ban->mask,
3574 MATCH_USENICK | MATCH_VISIBLE);
3577 for( ; ban && !match_ircglobs(mask, ban->mask);
3582 del_channel_ban(ban);
3589 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3591 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3597 static CHANSERV_FUNC(cmd_unban)
3599 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3602 static CHANSERV_FUNC(cmd_delban)
3604 /* it doesn't necessarily have to remove the channel ban - may want
3605 to make that an option. */
3606 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3609 static CHANSERV_FUNC(cmd_unbanme)
3611 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3612 long flags = ACTION_UNBAN;
3614 /* remove permanent bans if the user has the proper access. */
3615 if(uData->access >= UL_MASTER)
3616 flags |= ACTION_DEL_BAN;
3618 argv[1] = user->nick;
3619 return unban_user(user, channel, 2, argv, cmd, flags);
3622 static CHANSERV_FUNC(cmd_unbanall)
3624 struct mod_chanmode *change;
3627 if(!channel->banlist.used)
3629 reply("CSMSG_NO_BANS", channel->name);
3633 change = mod_chanmode_alloc(channel->banlist.used);
3634 for(ii=0; ii<channel->banlist.used; ii++)
3636 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3637 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3639 modcmd_chanmode_announce(change);
3640 for(ii = 0; ii < change->argc; ++ii)
3641 free((char*)change->args[ii].u.hostmask);
3642 mod_chanmode_free(change);
3643 reply("CSMSG_BANS_REMOVED", channel->name);
3647 static CHANSERV_FUNC(cmd_open)
3649 struct mod_chanmode *change;
3652 change = find_matching_bans(&channel->banlist, user, NULL);
3654 change = mod_chanmode_alloc(0);
3655 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3656 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3657 && channel->channel_info->modes.modes_set)
3658 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3659 modcmd_chanmode_announce(change);
3660 reply("CSMSG_CHANNEL_OPENED", channel->name);
3661 for(ii = 0; ii < change->argc; ++ii)
3662 free((char*)change->args[ii].u.hostmask);
3663 mod_chanmode_free(change);
3667 static CHANSERV_FUNC(cmd_myaccess)
3669 static struct string_buffer sbuf;
3670 struct handle_info *target_handle;
3671 struct userData *uData;
3674 target_handle = user->handle_info;
3675 else if(!IsStaff(user))
3677 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3680 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3683 if(!oper_outranks(user, target_handle))
3686 if(!target_handle->channels)
3688 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3692 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3693 for(uData = target_handle->channels; uData; uData = uData->u_next)
3695 struct chanData *cData = uData->channel;
3697 if(uData->access > UL_OWNER)
3699 if(IsProtected(cData)
3700 && (target_handle != user->handle_info)
3701 && !GetTrueChannelAccess(cData, user->handle_info))
3704 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3705 if(uData->flags != USER_AUTO_OP)
3706 string_buffer_append(&sbuf, ',');
3707 if(IsUserSuspended(uData))
3708 string_buffer_append(&sbuf, 's');
3709 if(IsUserAutoOp(uData))
3711 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3712 string_buffer_append(&sbuf, 'o');
3713 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3714 string_buffer_append(&sbuf, 'v');
3716 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3717 string_buffer_append(&sbuf, 'i');
3719 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3721 string_buffer_append_string(&sbuf, ")]");
3722 string_buffer_append(&sbuf, '\0');
3723 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3729 static CHANSERV_FUNC(cmd_access)
3731 struct userNode *target;
3732 struct handle_info *target_handle;
3733 struct userData *uData;
3735 char prefix[MAXLEN];
3740 target_handle = target->handle_info;
3742 else if((target = GetUserH(argv[1])))
3744 target_handle = target->handle_info;
3746 else if(argv[1][0] == '*')
3748 if(!(target_handle = get_handle_info(argv[1]+1)))
3750 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3756 reply("MSG_NICK_UNKNOWN", argv[1]);
3760 assert(target || target_handle);
3762 if(target == chanserv)
3764 reply("CSMSG_IS_CHANSERV");
3772 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3777 reply("MSG_USER_AUTHENTICATE", target->nick);
3780 reply("MSG_AUTHENTICATE");
3786 const char *epithet = NULL, *type = NULL;
3789 epithet = chanserv_conf.irc_operator_epithet;
3790 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3792 else if(IsNetworkHelper(target))
3794 epithet = chanserv_conf.network_helper_epithet;
3795 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3797 else if(IsSupportHelper(target))
3799 epithet = chanserv_conf.support_helper_epithet;
3800 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3804 if(target_handle->epithet)
3805 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3807 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3809 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3813 sprintf(prefix, "%s", target_handle->handle);
3816 if(!channel->channel_info)
3818 reply("CSMSG_NOT_REGISTERED", channel->name);
3822 helping = HANDLE_FLAGGED(target_handle, HELPING)
3823 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3824 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3826 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3827 /* To prevent possible information leaks, only show infolines
3828 * if the requestor is in the channel or it's their own
3830 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3832 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3834 /* Likewise, only say it's suspended if the user has active
3835 * access in that channel or it's their own entry. */
3836 if(IsUserSuspended(uData)
3837 && (GetChannelUser(channel->channel_info, user->handle_info)
3838 || (user->handle_info == uData->handle)))
3840 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3845 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3852 zoot_list(struct listData *list)
3854 struct userData *uData;
3855 unsigned int start, curr, highest, lowest;
3856 struct helpfile_table tmp_table;
3857 const char **temp, *msg;
3859 if(list->table.length == 1)
3862 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3864 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3865 msg = user_find_message(list->user, "MSG_NONE");
3866 send_message_type(4, list->user, list->bot, " %s", msg);
3868 tmp_table.width = list->table.width;
3869 tmp_table.flags = list->table.flags;
3870 list->table.contents[0][0] = " ";
3871 highest = list->highest;
3872 if(list->lowest != 0)
3873 lowest = list->lowest;
3874 else if(highest < 100)
3877 lowest = highest - 100;
3878 for(start = curr = 1; curr < list->table.length; )
3880 uData = list->users[curr-1];
3881 list->table.contents[curr++][0] = " ";
3882 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3885 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3887 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3888 temp = list->table.contents[--start];
3889 list->table.contents[start] = list->table.contents[0];
3890 tmp_table.contents = list->table.contents + start;
3891 tmp_table.length = curr - start;
3892 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3893 list->table.contents[start] = temp;
3895 highest = lowest - 1;
3896 lowest = (highest < 100) ? 0 : (highest - 99);
3902 def_list(struct listData *list)
3906 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3908 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3909 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3910 if(list->table.length == 1)
3912 msg = user_find_message(list->user, "MSG_NONE");
3913 send_message_type(4, list->user, list->bot, " %s", msg);
3918 userData_access_comp(const void *arg_a, const void *arg_b)
3920 const struct userData *a = *(struct userData**)arg_a;
3921 const struct userData *b = *(struct userData**)arg_b;
3923 if(a->access != b->access)
3924 res = b->access - a->access;
3926 res = irccasecmp(a->handle->handle, b->handle->handle);
3931 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3933 void (*send_list)(struct listData *);
3934 struct userData *uData;
3935 struct listData lData;
3936 unsigned int matches;
3940 lData.bot = cmd->parent->bot;
3941 lData.channel = channel;
3942 lData.lowest = lowest;
3943 lData.highest = highest;
3944 lData.search = (argc > 1) ? argv[1] : NULL;
3945 send_list = def_list;
3946 (void)zoot_list; /* since it doesn't show user levels */
3948 if(user->handle_info)
3950 switch(user->handle_info->userlist_style)
3952 case HI_STYLE_DEF: send_list = def_list; break;
3953 case HI_STYLE_ZOOT: send_list = def_list; break;
3957 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3959 for(uData = channel->channel_info->users; uData; uData = uData->next)
3961 if((uData->access < lowest)
3962 || (uData->access > highest)
3963 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3965 lData.users[matches++] = uData;
3967 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3969 lData.table.length = matches+1;
3970 lData.table.width = 4;
3971 lData.table.flags = TABLE_NO_FREE;
3972 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3973 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3974 lData.table.contents[0] = ary;
3977 ary[2] = "Last Seen";
3979 for(matches = 1; matches < lData.table.length; ++matches)
3981 char seen[INTERVALLEN];
3983 uData = lData.users[matches-1];
3984 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3985 lData.table.contents[matches] = ary;
3986 ary[0] = strtab(uData->access);
3987 ary[1] = uData->handle->handle;
3990 else if(!uData->seen)
3993 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3994 ary[2] = strdup(ary[2]);
3995 if(IsUserSuspended(uData))
3996 ary[3] = "Suspended";
3997 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3998 ary[3] = "Vacation";
3999 else if(HANDLE_FLAGGED(uData->handle, BOT))
4005 for(matches = 1; matches < lData.table.length; ++matches)
4007 free((char*)lData.table.contents[matches][2]);
4008 free(lData.table.contents[matches]);
4010 free(lData.table.contents[0]);
4011 free(lData.table.contents);
4015 static CHANSERV_FUNC(cmd_users)
4017 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4020 static CHANSERV_FUNC(cmd_wlist)
4022 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4025 static CHANSERV_FUNC(cmd_clist)
4027 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4030 static CHANSERV_FUNC(cmd_mlist)
4032 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4035 static CHANSERV_FUNC(cmd_olist)
4037 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4040 static CHANSERV_FUNC(cmd_plist)
4042 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4045 static CHANSERV_FUNC(cmd_bans)
4047 struct userNode *search_u = NULL;
4048 struct helpfile_table tbl;
4049 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4050 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4051 const char *msg_never, *triggered, *expires;
4052 struct banData *ban, **bans;
4056 else if(strchr(search = argv[1], '!'))
4059 search_wilds = search[strcspn(search, "?*")];
4061 else if(!(search_u = GetUserH(search)))
4062 reply("MSG_NICK_UNKNOWN", search);
4064 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4066 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4070 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4075 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4078 bans[matches++] = ban;
4083 tbl.length = matches + 1;
4084 tbl.width = 4 + timed;
4086 tbl.flags = TABLE_NO_FREE;
4087 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4088 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4089 tbl.contents[0][0] = "Mask";
4090 tbl.contents[0][1] = "Set By";
4091 tbl.contents[0][2] = "Triggered";
4094 tbl.contents[0][3] = "Expires";
4095 tbl.contents[0][4] = "Reason";
4098 tbl.contents[0][3] = "Reason";
4101 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4103 free(tbl.contents[0]);
4108 msg_never = user_find_message(user, "MSG_NEVER");
4109 for(ii = 0; ii < matches; )
4115 else if(ban->expires)
4116 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4118 expires = msg_never;
4121 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4123 triggered = msg_never;
4125 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4126 tbl.contents[ii][0] = ban->mask;
4127 tbl.contents[ii][1] = ban->owner;
4128 tbl.contents[ii][2] = strdup(triggered);
4131 tbl.contents[ii][3] = strdup(expires);
4132 tbl.contents[ii][4] = ban->reason;
4135 tbl.contents[ii][3] = ban->reason;
4137 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4138 reply("MSG_MATCH_COUNT", matches);
4139 for(ii = 1; ii < tbl.length; ++ii)
4141 free((char*)tbl.contents[ii][2]);
4143 free((char*)tbl.contents[ii][3]);
4144 free(tbl.contents[ii]);
4146 free(tbl.contents[0]);
4152 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4154 struct chanData *cData = channel->channel_info;
4155 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4157 if(cData->topic_mask)
4158 return !match_ircglob(new_topic, cData->topic_mask);
4159 else if(cData->topic)
4160 return irccasecmp(new_topic, cData->topic);
4165 static CHANSERV_FUNC(cmd_topic)
4167 struct chanData *cData;
4170 cData = channel->channel_info;
4175 SetChannelTopic(channel, chanserv, cData->topic, 1);
4176 reply("CSMSG_TOPIC_SET", cData->topic);
4180 reply("CSMSG_NO_TOPIC", channel->name);
4184 topic = unsplit_string(argv + 1, argc - 1, NULL);
4185 /* If they say "!topic *", use an empty topic. */
4186 if((topic[0] == '*') && (topic[1] == 0))
4188 if(bad_topic(channel, user, topic))
4190 char *topic_mask = cData->topic_mask;
4193 char new_topic[TOPICLEN+1], tchar;
4194 int pos=0, starpos=-1, dpos=0, len;
4196 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4203 len = strlen(topic);
4204 if((dpos + len) > TOPICLEN)
4205 len = TOPICLEN + 1 - dpos;
4206 memcpy(new_topic+dpos, topic, len);
4210 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4211 default: new_topic[dpos++] = tchar; break;
4214 if((dpos > TOPICLEN) || tchar)
4217 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4218 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4221 new_topic[dpos] = 0;
4222 SetChannelTopic(channel, chanserv, new_topic, 1);
4224 reply("CSMSG_TOPIC_LOCKED", channel->name);
4229 SetChannelTopic(channel, chanserv, topic, 1);
4231 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4233 /* Grab the topic and save it as the default topic. */
4235 cData->topic = strdup(channel->topic);
4241 static CHANSERV_FUNC(cmd_mode)
4243 struct userData *uData;
4244 struct mod_chanmode *change;
4250 change = &channel->channel_info->modes;
4251 if(change->modes_set || change->modes_clear) {
4252 modcmd_chanmode_announce(change);
4253 reply("CSMSG_DEFAULTED_MODES", channel->name);
4255 reply("CSMSG_NO_MODES", channel->name);
4259 uData = GetChannelUser(channel->channel_info, user->handle_info);
4261 base_oplevel = MAXOPLEVEL;
4262 else if (uData->access >= UL_OWNER)
4265 base_oplevel = 1 + UL_OWNER - uData->access;
4266 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4269 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4273 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4274 && mode_lock_violated(&channel->channel_info->modes, change))
4277 mod_chanmode_format(&channel->channel_info->modes, modes);
4278 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4282 modcmd_chanmode_announce(change);
4283 mod_chanmode_format(change, fmt);
4284 mod_chanmode_free(change);
4285 reply("CSMSG_MODES_SET", fmt);
4289 static CHANSERV_FUNC(cmd_invite)
4291 struct userData *uData;
4292 struct userNode *invite;
4294 uData = GetChannelUser(channel->channel_info, user->handle_info);
4298 if(!(invite = GetUserH(argv[1])))
4300 reply("MSG_NICK_UNKNOWN", argv[1]);
4307 if(GetUserMode(channel, invite))
4309 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4317 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4318 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4321 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4323 irc_invite(chanserv, invite, channel);
4325 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4330 static CHANSERV_FUNC(cmd_inviteme)
4332 if(GetUserMode(channel, user))
4334 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4337 if(channel->channel_info
4338 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4340 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4343 irc_invite(cmd->parent->bot, user, channel);
4348 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4351 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4353 /* We display things based on two dimensions:
4354 * - Issue time: present or absent
4355 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4356 * (in order of precedence, so something both expired and revoked
4357 * only counts as revoked)
4359 combo = (suspended->issued ? 4 : 0)
4360 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4362 case 0: /* no issue time, indefinite expiration */
4363 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4365 case 1: /* no issue time, expires in future */
4366 intervalString(buf1, suspended->expires-now, user->handle_info);
4367 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4369 case 2: /* no issue time, expired */
4370 intervalString(buf1, now-suspended->expires, user->handle_info);
4371 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4373 case 3: /* no issue time, revoked */
4374 intervalString(buf1, now-suspended->revoked, user->handle_info);
4375 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4377 case 4: /* issue time set, indefinite expiration */
4378 intervalString(buf1, now-suspended->issued, user->handle_info);
4379 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4381 case 5: /* issue time set, expires in future */
4382 intervalString(buf1, now-suspended->issued, user->handle_info);
4383 intervalString(buf2, suspended->expires-now, user->handle_info);
4384 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4386 case 6: /* issue time set, expired */
4387 intervalString(buf1, now-suspended->issued, user->handle_info);
4388 intervalString(buf2, now-suspended->expires, user->handle_info);
4389 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4391 case 7: /* issue time set, revoked */
4392 intervalString(buf1, now-suspended->issued, user->handle_info);
4393 intervalString(buf2, now-suspended->revoked, user->handle_info);
4394 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4397 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4402 static CHANSERV_FUNC(cmd_info)
4404 char modes[MAXLEN], buffer[INTERVALLEN];
4405 struct userData *uData, *owner;
4406 struct chanData *cData;
4407 struct do_not_register *dnr;
4412 cData = channel->channel_info;
4413 reply("CSMSG_CHANNEL_INFO", channel->name);
4415 uData = GetChannelUser(cData, user->handle_info);
4416 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4418 mod_chanmode_format(&cData->modes, modes);
4419 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4420 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4423 for(it = dict_first(cData->notes); it; it = iter_next(it))
4427 note = iter_data(it);
4428 if(!note_type_visible_to_user(cData, note->type, user))
4431 padding = PADLEN - 1 - strlen(iter_key(it));
4432 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4435 reply("CSMSG_CHANNEL_MAX", cData->max);
4436 for(owner = cData->users; owner; owner = owner->next)
4437 if(owner->access == UL_OWNER)
4438 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4439 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4440 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4441 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4443 privileged = IsStaff(user);
4445 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4446 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4447 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4449 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4450 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4452 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4454 struct suspended *suspended;
4455 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4456 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4457 show_suspension_info(cmd, user, suspended);
4459 else if(IsSuspended(cData))
4461 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4462 show_suspension_info(cmd, user, cData->suspended);
4467 static CHANSERV_FUNC(cmd_netinfo)
4469 extern unsigned long boot_time;
4470 extern unsigned long burst_length;
4471 char interval[INTERVALLEN];
4473 reply("CSMSG_NETWORK_INFO");
4474 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4475 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4476 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4477 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4478 reply("CSMSG_NETWORK_BANS", banCount);
4479 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4480 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4481 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4486 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4488 struct helpfile_table table;
4490 struct userNode *user;
4495 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4496 table.contents = alloca(list->used*sizeof(*table.contents));
4497 for(nn=0; nn<list->used; nn++)
4499 user = list->list[nn];
4500 if(user->modes & skip_flags)
4504 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4507 nick = alloca(strlen(user->nick)+3);
4508 sprintf(nick, "(%s)", user->nick);
4512 table.contents[table.length][0] = nick;
4515 table_send(chanserv, to->nick, 0, NULL, table);
4518 static CHANSERV_FUNC(cmd_ircops)
4520 reply("CSMSG_STAFF_OPERS");
4521 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4525 static CHANSERV_FUNC(cmd_helpers)
4527 reply("CSMSG_STAFF_HELPERS");
4528 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4532 static CHANSERV_FUNC(cmd_staff)
4534 reply("CSMSG_NETWORK_STAFF");
4535 cmd_ircops(CSFUNC_ARGS);
4536 cmd_helpers(CSFUNC_ARGS);
4540 static CHANSERV_FUNC(cmd_peek)
4542 struct modeNode *mn;
4543 char modes[MODELEN];
4545 struct helpfile_table table;
4546 int opcount = 0, voicecount = 0, srvcount = 0;
4548 irc_make_chanmode(channel, modes);
4550 reply("CSMSG_PEEK_INFO", channel->name);
4551 reply("CSMSG_PEEK_TOPIC", channel->topic);
4552 reply("CSMSG_PEEK_MODES", modes);
4556 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4557 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4558 for(n = 0; n < channel->members.used; n++)
4560 mn = channel->members.list[n];
4561 if(IsLocal(mn->user))
4563 else if(mn->modes & MODE_CHANOP)
4565 else if(mn->modes & MODE_VOICE)
4568 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4570 table.contents[table.length] = alloca(sizeof(**table.contents));
4571 table.contents[table.length][0] = mn->user->nick;
4575 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4576 (channel->members.used - opcount - voicecount - srvcount));
4580 reply("CSMSG_PEEK_OPS");
4581 table_send(chanserv, user->nick, 0, NULL, table);
4584 reply("CSMSG_PEEK_NO_OPS");
4588 static MODCMD_FUNC(cmd_wipeinfo)
4590 struct handle_info *victim;
4591 struct userData *ud, *actor, *real_actor;
4592 unsigned int override = 0;
4595 actor = GetChannelUser(channel->channel_info, user->handle_info);
4596 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4597 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4599 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4601 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4604 if((ud->access >= actor->access) && (ud != actor))
4606 reply("MSG_USER_OUTRANKED", victim->handle);
4609 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4610 override = CMD_LOG_OVERRIDE;
4614 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4615 return 1 | override;
4618 static CHANSERV_FUNC(cmd_resync)
4620 struct mod_chanmode *changes;
4621 struct chanData *cData = channel->channel_info;
4622 unsigned int ii, used;
4624 changes = mod_chanmode_alloc(channel->members.used * 2);
4625 for(ii = used = 0; ii < channel->members.used; ++ii)
4627 struct modeNode *mn = channel->members.list[ii];
4628 struct userData *uData;
4630 if(IsService(mn->user))
4633 uData = GetChannelAccess(cData, mn->user->handle_info);
4634 if(!cData->lvlOpts[lvlGiveOps]
4635 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4637 if(!(mn->modes & MODE_CHANOP))
4639 changes->args[used].mode = MODE_CHANOP;
4640 changes->args[used++].u.member = mn;
4643 else if(!cData->lvlOpts[lvlGiveVoice]
4644 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4646 if(mn->modes & MODE_CHANOP)
4648 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4649 changes->args[used++].u.member = mn;
4651 if(!(mn->modes & MODE_VOICE))
4653 changes->args[used].mode = MODE_VOICE;
4654 changes->args[used++].u.member = mn;
4661 changes->args[used].mode = MODE_REMOVE | mn->modes;
4662 changes->args[used++].u.member = mn;
4666 changes->argc = used;
4667 modcmd_chanmode_announce(changes);
4668 mod_chanmode_free(changes);
4669 reply("CSMSG_RESYNCED_USERS", channel->name);
4673 static CHANSERV_FUNC(cmd_seen)
4675 struct userData *uData;
4676 struct handle_info *handle;
4677 char seen[INTERVALLEN];
4681 if(!irccasecmp(argv[1], chanserv->nick))
4683 reply("CSMSG_IS_CHANSERV");
4687 if(!(handle = get_handle_info(argv[1])))
4689 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4693 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4695 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4700 reply("CSMSG_USER_PRESENT", handle->handle);
4701 else if(uData->seen)
4702 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4704 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4706 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4707 reply("CSMSG_USER_VACATION", handle->handle);
4712 static MODCMD_FUNC(cmd_names)
4714 struct userNode *targ;
4715 struct userData *targData;
4716 unsigned int ii, pos;
4719 for(ii=pos=0; ii<channel->members.used; ++ii)
4721 targ = channel->members.list[ii]->user;
4722 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4725 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4728 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4732 if(IsUserSuspended(targData))
4734 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4737 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4738 reply("CSMSG_END_NAMES", channel->name);
4743 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4745 switch(ntype->visible_type)
4747 case NOTE_VIS_ALL: return 1;
4748 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4749 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4754 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4756 struct userData *uData;
4758 switch(ntype->set_access_type)
4760 case NOTE_SET_CHANNEL_ACCESS:
4761 if(!user->handle_info)
4763 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4765 return uData->access >= ntype->set_access.min_ulevel;
4766 case NOTE_SET_CHANNEL_SETTER:
4767 return check_user_level(channel, user, lvlSetters, 1, 0);
4768 case NOTE_SET_PRIVILEGED: default:
4769 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4773 static CHANSERV_FUNC(cmd_note)
4775 struct chanData *cData;
4777 struct note_type *ntype;
4779 cData = channel->channel_info;
4782 reply("CSMSG_NOT_REGISTERED", channel->name);
4786 /* If no arguments, show all visible notes for the channel. */
4792 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4794 note = iter_data(it);
4795 if(!note_type_visible_to_user(cData, note->type, user))
4798 reply("CSMSG_NOTELIST_HEADER", channel->name);
4799 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4802 reply("CSMSG_NOTELIST_END", channel->name);
4804 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4806 /* If one argument, show the named note. */
4809 if((note = dict_find(cData->notes, argv[1], NULL))
4810 && note_type_visible_to_user(cData, note->type, user))
4812 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4814 else if((ntype = dict_find(note_types, argv[1], NULL))
4815 && note_type_visible_to_user(NULL, ntype, user))
4817 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4822 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4826 /* Assume they're trying to set a note. */
4830 ntype = dict_find(note_types, argv[1], NULL);
4833 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4836 else if(note_type_settable_by_user(channel, ntype, user))
4838 note_text = unsplit_string(argv+2, argc-2, NULL);
4839 if((note = dict_find(cData->notes, argv[1], NULL)))
4840 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4841 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4842 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4844 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4846 /* The note is viewable to staff only, so return 0
4847 to keep the invocation from getting logged (or
4848 regular users can see it in !events). */
4854 reply("CSMSG_NO_ACCESS");
4861 static CHANSERV_FUNC(cmd_delnote)
4866 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4867 || !note_type_settable_by_user(channel, note->type, user))
4869 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4872 dict_remove(channel->channel_info->notes, note->type->name);
4873 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4877 static CHANSERV_FUNC(cmd_events)
4879 struct logSearch discrim;
4880 struct logReport report;
4881 unsigned int matches, limit;
4883 limit = (argc > 1) ? atoi(argv[1]) : 10;
4884 if(limit < 1 || limit > 200)
4887 memset(&discrim, 0, sizeof(discrim));
4888 discrim.masks.bot = chanserv;
4889 discrim.masks.channel_name = channel->name;
4891 discrim.masks.command = argv[2];
4892 discrim.limit = limit;
4893 discrim.max_time = INT_MAX;
4894 discrim.severities = 1 << LOG_COMMAND;
4895 report.reporter = chanserv;
4897 reply("CSMSG_EVENT_SEARCH_RESULTS");
4898 matches = log_entry_search(&discrim, log_report_entry, &report);
4900 reply("MSG_MATCH_COUNT", matches);
4902 reply("MSG_NO_MATCHES");
4906 static CHANSERV_FUNC(cmd_say)
4912 msg = unsplit_string(argv + 1, argc - 1, NULL);
4913 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4915 else if(*argv[1] == '*' && argv[1][1] != '\0')
4917 struct handle_info *hi;
4918 struct userNode *authed;
4921 msg = unsplit_string(argv + 2, argc - 2, NULL);
4923 if (!(hi = get_handle_info(argv[1] + 1)))
4925 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4929 for (authed = hi->users; authed; authed = authed->next_authed)
4930 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4932 else if(GetUserH(argv[1]))
4935 msg = unsplit_string(argv + 2, argc - 2, NULL);
4936 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4940 reply("MSG_NOT_TARGET_NAME");
4946 static CHANSERV_FUNC(cmd_emote)
4952 /* CTCP is so annoying. */
4953 msg = unsplit_string(argv + 1, argc - 1, NULL);
4954 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4956 else if(*argv[1] == '*' && argv[1][1] != '\0')
4958 struct handle_info *hi;
4959 struct userNode *authed;
4962 msg = unsplit_string(argv + 2, argc - 2, NULL);
4964 if (!(hi = get_handle_info(argv[1] + 1)))
4966 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4970 for (authed = hi->users; authed; authed = authed->next_authed)
4971 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4973 else if(GetUserH(argv[1]))
4975 msg = unsplit_string(argv + 2, argc - 2, NULL);
4976 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4980 reply("MSG_NOT_TARGET_NAME");
4986 struct channelList *
4987 chanserv_support_channels(void)
4989 return &chanserv_conf.support_channels;
4992 static CHANSERV_FUNC(cmd_expire)
4994 int channel_count = registered_channels;
4995 expire_channels(NULL);
4996 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5001 chanserv_expire_suspension(void *data)
5003 struct suspended *suspended = data;
5004 struct chanNode *channel;
5007 /* Update the channel registration data structure. */
5008 if(!suspended->expires || (now < suspended->expires))
5009 suspended->revoked = now;
5010 channel = suspended->cData->channel;
5011 suspended->cData->channel = channel;
5012 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5014 /* If appropriate, re-join ChanServ to the channel. */
5015 if(!IsOffChannel(suspended->cData))
5017 spamserv_cs_suspend(channel, 0, 0, NULL);
5018 ss_cs_join_channel(channel, 1);
5021 /* Mark everyone currently in the channel as present. */
5022 for(ii = 0; ii < channel->members.used; ++ii)
5024 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5033 static CHANSERV_FUNC(cmd_csuspend)
5035 struct suspended *suspended;
5036 char reason[MAXLEN];
5037 unsigned long expiry, duration;
5038 struct userData *uData;
5042 if(IsProtected(channel->channel_info))
5044 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5048 if(argv[1][0] == '!')
5050 else if(IsSuspended(channel->channel_info))
5052 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5053 show_suspension_info(cmd, user, channel->channel_info->suspended);
5057 if(!strcmp(argv[1], "0"))
5059 else if((duration = ParseInterval(argv[1])))
5060 expiry = now + duration;
5063 reply("MSG_INVALID_DURATION", argv[1]);
5067 unsplit_string(argv + 2, argc - 2, reason);
5069 suspended = calloc(1, sizeof(*suspended));
5070 suspended->revoked = 0;
5071 suspended->issued = now;
5072 suspended->suspender = strdup(user->handle_info->handle);
5073 suspended->expires = expiry;
5074 suspended->reason = strdup(reason);
5075 suspended->cData = channel->channel_info;
5076 suspended->previous = suspended->cData->suspended;
5077 suspended->cData->suspended = suspended;
5079 if(suspended->expires)
5080 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5082 if(IsSuspended(channel->channel_info))
5084 suspended->previous->revoked = now;
5085 if(suspended->previous->expires)
5086 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5087 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5088 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5092 /* Mark all users in channel as absent. */
5093 for(uData = channel->channel_info->users; uData; uData = uData->next)
5102 /* Mark the channel as suspended, then part. */
5103 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5104 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5105 DelChannelUser(chanserv, channel, suspended->reason, 0);
5106 reply("CSMSG_SUSPENDED", channel->name);
5107 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5108 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5113 static CHANSERV_FUNC(cmd_cunsuspend)
5115 struct suspended *suspended;
5116 char message[MAXLEN];
5118 if(!IsSuspended(channel->channel_info))
5120 reply("CSMSG_NOT_SUSPENDED", channel->name);
5124 suspended = channel->channel_info->suspended;
5126 /* Expire the suspension and join ChanServ to the channel. */
5127 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5128 chanserv_expire_suspension(suspended);
5129 reply("CSMSG_UNSUSPENDED", channel->name);
5130 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5131 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5135 typedef struct chanservSearch
5140 unsigned long unvisited;
5141 unsigned long registered;
5143 unsigned long flags;
5147 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5150 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5155 search = malloc(sizeof(struct chanservSearch));
5156 memset(search, 0, sizeof(*search));
5159 for(i = 0; i < argc; i++)
5161 /* Assume all criteria require arguments. */
5164 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5168 if(!irccasecmp(argv[i], "name"))
5169 search->name = argv[++i];
5170 else if(!irccasecmp(argv[i], "registrar"))
5171 search->registrar = argv[++i];
5172 else if(!irccasecmp(argv[i], "unvisited"))
5173 search->unvisited = ParseInterval(argv[++i]);
5174 else if(!irccasecmp(argv[i], "registered"))
5175 search->registered = ParseInterval(argv[++i]);
5176 else if(!irccasecmp(argv[i], "flags"))
5179 if(!irccasecmp(argv[i], "nodelete"))
5180 search->flags |= CHANNEL_NODELETE;
5181 else if(!irccasecmp(argv[i], "suspended"))
5182 search->flags |= CHANNEL_SUSPENDED;
5183 else if(!irccasecmp(argv[i], "unreviewed"))
5184 search->flags |= CHANNEL_UNREVIEWED;
5187 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5191 else if(!irccasecmp(argv[i], "limit"))
5192 search->limit = strtoul(argv[++i], NULL, 10);
5195 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5200 if(search->name && !strcmp(search->name, "*"))
5202 if(search->registrar && !strcmp(search->registrar, "*"))
5203 search->registrar = 0;
5212 chanserv_channel_match(struct chanData *channel, search_t search)
5214 const char *name = channel->channel->name;
5215 if((search->name && !match_ircglob(name, search->name)) ||
5216 (search->registrar && !channel->registrar) ||
5217 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5218 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5219 (search->registered && (now - channel->registered) > search->registered) ||
5220 (search->flags && ((search->flags & channel->flags) != search->flags)))
5227 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5229 struct chanData *channel;
5230 unsigned int matches = 0;
5232 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5234 if(!chanserv_channel_match(channel, search))
5244 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5249 search_print(struct chanData *channel, void *data)
5251 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5254 static CHANSERV_FUNC(cmd_search)
5257 unsigned int matches;
5258 channel_search_func action;
5262 if(!irccasecmp(argv[1], "count"))
5263 action = search_count;
5264 else if(!irccasecmp(argv[1], "print"))
5265 action = search_print;
5268 reply("CSMSG_ACTION_INVALID", argv[1]);
5272 search = chanserv_search_create(user, argc - 2, argv + 2);
5276 if(action == search_count)
5277 search->limit = INT_MAX;
5279 if(action == search_print)
5280 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5282 matches = chanserv_channel_search(search, action, user);
5285 reply("MSG_MATCH_COUNT", matches);
5287 reply("MSG_NO_MATCHES");
5293 static CHANSERV_FUNC(cmd_unvisited)
5295 struct chanData *cData;
5296 unsigned long interval = chanserv_conf.channel_expire_delay;
5297 char buffer[INTERVALLEN];
5298 unsigned int limit = 25, matches = 0;
5302 interval = ParseInterval(argv[1]);
5304 limit = atoi(argv[2]);
5307 intervalString(buffer, interval, user->handle_info);
5308 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5310 for(cData = channelList; cData && matches < limit; cData = cData->next)
5312 if((now - cData->visited) < interval)
5315 intervalString(buffer, now - cData->visited, user->handle_info);
5316 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5323 static MODCMD_FUNC(chan_opt_defaulttopic)
5329 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5331 reply("CSMSG_TOPIC_LOCKED", channel->name);
5335 topic = unsplit_string(argv+1, argc-1, NULL);
5337 free(channel->channel_info->topic);
5338 if(topic[0] == '*' && topic[1] == 0)
5340 topic = channel->channel_info->topic = NULL;
5344 topic = channel->channel_info->topic = strdup(topic);
5345 if(channel->channel_info->topic_mask
5346 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5347 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5349 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5352 if(channel->channel_info->topic)
5353 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5355 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5359 static MODCMD_FUNC(chan_opt_topicmask)
5363 struct chanData *cData = channel->channel_info;
5366 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5368 reply("CSMSG_TOPIC_LOCKED", channel->name);
5372 mask = unsplit_string(argv+1, argc-1, NULL);
5374 if(cData->topic_mask)
5375 free(cData->topic_mask);
5376 if(mask[0] == '*' && mask[1] == 0)
5378 cData->topic_mask = 0;
5382 cData->topic_mask = strdup(mask);
5384 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5385 else if(!match_ircglob(cData->topic, cData->topic_mask))
5386 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5390 if(channel->channel_info->topic_mask)
5391 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5393 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5397 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5401 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5405 if(greeting[0] == '*' && greeting[1] == 0)
5409 unsigned int length = strlen(greeting);
5410 if(length > chanserv_conf.greeting_length)
5412 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5415 *data = strdup(greeting);
5424 reply(name, user_find_message(user, "MSG_NONE"));
5428 static MODCMD_FUNC(chan_opt_greeting)
5430 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5433 static MODCMD_FUNC(chan_opt_usergreeting)
5435 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5438 static MODCMD_FUNC(chan_opt_modes)
5440 struct mod_chanmode *new_modes;
5445 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5447 reply("CSMSG_NO_ACCESS");
5450 if(argv[1][0] == '*' && argv[1][1] == 0)
5452 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5454 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5456 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5459 else if(new_modes->argc > 1)
5461 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5462 mod_chanmode_free(new_modes);
5467 channel->channel_info->modes = *new_modes;
5468 modcmd_chanmode_announce(new_modes);
5469 mod_chanmode_free(new_modes);
5473 mod_chanmode_format(&channel->channel_info->modes, modes);
5475 reply("CSMSG_SET_MODES", modes);
5477 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5481 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5483 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5485 struct chanData *cData = channel->channel_info;
5490 /* Set flag according to value. */
5491 if(enabled_string(argv[1]))
5493 cData->flags |= mask;
5496 else if(disabled_string(argv[1]))
5498 cData->flags &= ~mask;
5503 reply("MSG_INVALID_BINARY", argv[1]);
5509 /* Find current option value. */
5510 value = (cData->flags & mask) ? 1 : 0;
5514 reply(name, user_find_message(user, "MSG_ON"));
5516 reply(name, user_find_message(user, "MSG_OFF"));
5520 static MODCMD_FUNC(chan_opt_nodelete)
5522 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5524 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5528 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5531 static MODCMD_FUNC(chan_opt_dynlimit)
5533 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5536 static MODCMD_FUNC(chan_opt_offchannel)
5538 struct chanData *cData = channel->channel_info;
5543 /* Set flag according to value. */
5544 if(enabled_string(argv[1]))
5546 if(!IsOffChannel(cData))
5547 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5548 cData->flags |= CHANNEL_OFFCHANNEL;
5551 else if(disabled_string(argv[1]))
5553 if(IsOffChannel(cData))
5555 struct mod_chanmode change;
5556 mod_chanmode_init(&change);
5558 change.args[0].mode = MODE_CHANOP;
5559 change.args[0].u.member = AddChannelUser(chanserv, channel);
5560 mod_chanmode_announce(chanserv, channel, &change);
5562 cData->flags &= ~CHANNEL_OFFCHANNEL;
5567 reply("MSG_INVALID_BINARY", argv[1]);
5573 /* Find current option value. */
5574 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5578 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5580 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5584 static MODCMD_FUNC(chan_opt_unreviewed)
5586 struct chanData *cData = channel->channel_info;
5587 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5593 /* The two directions can have different ACLs. */
5594 if(enabled_string(argv[1]))
5596 else if(disabled_string(argv[1]))
5600 reply("MSG_INVALID_BINARY", argv[1]);
5604 if (new_value != value)
5606 struct svccmd *subcmd;
5607 char subcmd_name[32];
5609 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5610 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5613 reply("MSG_COMMAND_DISABLED", subcmd_name);
5616 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5620 cData->flags |= CHANNEL_UNREVIEWED;
5623 free(cData->registrar);
5624 cData->registrar = strdup(user->handle_info->handle);
5625 cData->flags &= ~CHANNEL_UNREVIEWED;
5632 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5634 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5638 static MODCMD_FUNC(chan_opt_defaults)
5640 struct userData *uData;
5641 struct chanData *cData;
5642 const char *confirm;
5643 enum levelOption lvlOpt;
5644 enum charOption chOpt;
5646 cData = channel->channel_info;
5647 uData = GetChannelUser(cData, user->handle_info);
5648 if(!uData || (uData->access < UL_OWNER))
5650 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5653 confirm = make_confirmation_string(uData);
5654 if((argc < 2) || strcmp(argv[1], confirm))
5656 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5659 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5660 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5661 cData->modes = chanserv_conf.default_modes;
5662 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5663 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5664 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5665 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5666 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5671 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5673 struct chanData *cData = channel->channel_info;
5674 struct userData *uData;
5675 unsigned short value;
5679 if(!check_user_level(channel, user, option, 1, 1))
5681 reply("CSMSG_CANNOT_SET");
5684 value = user_level_from_name(argv[1], UL_OWNER+1);
5685 if(!value && strcmp(argv[1], "0"))
5687 reply("CSMSG_INVALID_ACCESS", argv[1]);
5690 uData = GetChannelUser(cData, user->handle_info);
5691 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5693 reply("CSMSG_BAD_SETLEVEL");
5699 if(value > cData->lvlOpts[lvlGiveOps])
5701 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5706 if(value < cData->lvlOpts[lvlGiveVoice])
5708 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5713 /* This test only applies to owners, since non-owners
5714 * trying to set an option to above their level get caught
5715 * by the CSMSG_BAD_SETLEVEL test above.
5717 if(value > uData->access)
5719 reply("CSMSG_BAD_SETTERS");
5726 cData->lvlOpts[option] = value;
5728 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5732 static MODCMD_FUNC(chan_opt_enfops)
5734 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5737 static MODCMD_FUNC(chan_opt_giveops)
5739 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5742 static MODCMD_FUNC(chan_opt_enfmodes)
5744 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5747 static MODCMD_FUNC(chan_opt_enftopic)
5749 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5752 static MODCMD_FUNC(chan_opt_pubcmd)
5754 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5757 static MODCMD_FUNC(chan_opt_setters)
5759 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5762 static MODCMD_FUNC(chan_opt_ctcpusers)
5764 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5767 static MODCMD_FUNC(chan_opt_userinfo)
5769 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5772 static MODCMD_FUNC(chan_opt_givevoice)
5774 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5777 static MODCMD_FUNC(chan_opt_topicsnarf)
5779 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5782 static MODCMD_FUNC(chan_opt_inviteme)
5784 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5788 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5790 struct chanData *cData = channel->channel_info;
5791 int count = charOptions[option].count, idx;
5795 idx = atoi(argv[1]);
5797 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5799 reply("CSMSG_INVALID_NUMERIC", idx);
5800 /* Show possible values. */
5801 for(idx = 0; idx < count; idx++)
5802 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5806 cData->chOpts[option] = charOptions[option].values[idx].value;
5810 /* Find current option value. */
5813 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5817 /* Somehow, the option value is corrupt; reset it to the default. */
5818 cData->chOpts[option] = charOptions[option].default_value;
5823 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5827 static MODCMD_FUNC(chan_opt_protect)
5829 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5832 static MODCMD_FUNC(chan_opt_toys)
5834 return channel_multiple_option(chToys, CSFUNC_ARGS);
5837 static MODCMD_FUNC(chan_opt_ctcpreaction)
5839 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5842 static MODCMD_FUNC(chan_opt_topicrefresh)
5844 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5847 static struct svccmd_list set_shows_list;
5850 handle_svccmd_unbind(struct svccmd *target) {
5852 for(ii=0; ii<set_shows_list.used; ++ii)
5853 if(target == set_shows_list.list[ii])
5854 set_shows_list.used = 0;
5857 static CHANSERV_FUNC(cmd_set)
5859 struct svccmd *subcmd;
5863 /* Check if we need to (re-)initialize set_shows_list. */
5864 if(!set_shows_list.used)
5866 if(!set_shows_list.size)
5868 set_shows_list.size = chanserv_conf.set_shows->used;
5869 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5871 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5873 const char *name = chanserv_conf.set_shows->list[ii];
5874 sprintf(buf, "%s %s", argv[0], name);
5875 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5878 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5881 svccmd_list_append(&set_shows_list, subcmd);
5887 reply("CSMSG_CHANNEL_OPTIONS");
5888 for(ii = 0; ii < set_shows_list.used; ii++)
5890 subcmd = set_shows_list.list[ii];
5891 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5896 sprintf(buf, "%s %s", argv[0], argv[1]);
5897 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5900 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5903 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5905 reply("CSMSG_NO_ACCESS");
5911 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5915 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5917 struct userData *uData;
5919 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5922 reply("CSMSG_NOT_USER", channel->name);
5928 /* Just show current option value. */
5930 else if(enabled_string(argv[1]))
5932 uData->flags |= mask;
5934 else if(disabled_string(argv[1]))
5936 uData->flags &= ~mask;
5940 reply("MSG_INVALID_BINARY", argv[1]);
5944 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5948 static MODCMD_FUNC(user_opt_noautoop)
5950 struct userData *uData;
5952 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5955 reply("CSMSG_NOT_USER", channel->name);
5958 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5959 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5961 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5964 static MODCMD_FUNC(user_opt_autoinvite)
5966 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5968 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5970 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5973 static MODCMD_FUNC(user_opt_info)
5975 struct userData *uData;
5978 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5982 /* If they got past the command restrictions (which require access)
5983 * but fail this test, we have some fool with security override on.
5985 reply("CSMSG_NOT_USER", channel->name);
5992 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5993 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5995 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5998 bp = strcspn(infoline, "\001");
6001 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6006 if(infoline[0] == '*' && infoline[1] == 0)
6009 uData->info = strdup(infoline);
6012 reply("CSMSG_USET_INFO", uData->info);
6014 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6018 struct svccmd_list uset_shows_list;
6020 static CHANSERV_FUNC(cmd_uset)
6022 struct svccmd *subcmd;
6026 /* Check if we need to (re-)initialize uset_shows_list. */
6027 if(!uset_shows_list.used)
6031 "NoAutoOp", "AutoInvite", "Info"
6034 if(!uset_shows_list.size)
6036 uset_shows_list.size = ArrayLength(options);
6037 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6039 for(ii = 0; ii < ArrayLength(options); ii++)
6041 const char *name = options[ii];
6042 sprintf(buf, "%s %s", argv[0], name);
6043 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6046 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6049 svccmd_list_append(&uset_shows_list, subcmd);
6055 /* Do this so options are presented in a consistent order. */
6056 reply("CSMSG_USER_OPTIONS");
6057 for(ii = 0; ii < uset_shows_list.used; ii++)
6058 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6062 sprintf(buf, "%s %s", argv[0], argv[1]);
6063 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6066 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6070 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6073 static CHANSERV_FUNC(cmd_giveownership)
6075 struct handle_info *new_owner_hi;
6076 struct userData *new_owner;
6077 struct userData *curr_user;
6078 struct userData *invoker;
6079 struct chanData *cData = channel->channel_info;
6080 struct do_not_register *dnr;
6081 const char *confirm;
6083 unsigned short co_access;
6084 char reason[MAXLEN];
6087 curr_user = GetChannelAccess(cData, user->handle_info);
6088 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6089 if(!curr_user || (curr_user->access != UL_OWNER))
6091 struct userData *owner = NULL;
6092 for(curr_user = channel->channel_info->users;
6094 curr_user = curr_user->next)
6096 if(curr_user->access != UL_OWNER)
6100 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6107 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6109 char delay[INTERVALLEN];
6110 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6111 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6114 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6116 if(new_owner_hi == user->handle_info)
6118 reply("CSMSG_NO_TRANSFER_SELF");
6121 new_owner = GetChannelAccess(cData, new_owner_hi);
6126 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6130 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6134 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6136 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6139 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6140 if(!IsHelping(user))
6141 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6143 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6146 invoker = GetChannelUser(cData, user->handle_info);
6147 if(invoker->access <= UL_OWNER)
6149 confirm = make_confirmation_string(curr_user);
6150 if((argc < 3) || strcmp(argv[2], confirm))
6152 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6156 if(new_owner->access >= UL_COOWNER)
6157 co_access = new_owner->access;
6159 co_access = UL_COOWNER;
6160 new_owner->access = UL_OWNER;
6162 curr_user->access = co_access;
6163 cData->ownerTransfer = now;
6164 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6165 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6166 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6170 static CHANSERV_FUNC(cmd_suspend)
6172 struct handle_info *hi;
6173 struct userData *actor, *real_actor, *target;
6174 unsigned int override = 0;
6177 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6178 actor = GetChannelUser(channel->channel_info, user->handle_info);
6179 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6180 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6182 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6185 if(target->access >= actor->access)
6187 reply("MSG_USER_OUTRANKED", hi->handle);
6190 if(target->flags & USER_SUSPENDED)
6192 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6197 target->present = 0;
6200 if(!real_actor || target->access >= real_actor->access)
6201 override = CMD_LOG_OVERRIDE;
6202 target->flags |= USER_SUSPENDED;
6203 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6204 return 1 | override;
6207 static CHANSERV_FUNC(cmd_unsuspend)
6209 struct handle_info *hi;
6210 struct userData *actor, *real_actor, *target;
6211 unsigned int override = 0;
6214 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6215 actor = GetChannelUser(channel->channel_info, user->handle_info);
6216 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6217 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6219 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6222 if(target->access >= actor->access)
6224 reply("MSG_USER_OUTRANKED", hi->handle);
6227 if(!(target->flags & USER_SUSPENDED))
6229 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6232 if(!real_actor || target->access >= real_actor->access)
6233 override = CMD_LOG_OVERRIDE;
6234 target->flags &= ~USER_SUSPENDED;
6235 scan_user_presence(target, NULL);
6236 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6237 return 1 | override;
6240 static MODCMD_FUNC(cmd_deleteme)
6242 struct handle_info *hi;
6243 struct userData *target;
6244 const char *confirm_string;
6245 unsigned short access_level;
6248 hi = user->handle_info;
6249 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6251 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6254 if(target->access == UL_OWNER)
6256 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6259 confirm_string = make_confirmation_string(target);
6260 if((argc < 2) || strcmp(argv[1], confirm_string))
6262 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6265 access_level = target->access;
6266 channel_name = strdup(channel->name);
6267 del_channel_user(target, 1);
6268 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6274 chanserv_refresh_topics(UNUSED_ARG(void *data))
6276 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6277 struct chanData *cData;
6280 for(cData = channelList; cData; cData = cData->next)
6282 if(IsSuspended(cData))
6284 opt = cData->chOpts[chTopicRefresh];
6287 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6290 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6291 cData->last_refresh = refresh_num;
6293 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6296 static CHANSERV_FUNC(cmd_unf)
6300 char response[MAXLEN];
6301 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6302 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6303 irc_privmsg(cmd->parent->bot, channel->name, response);
6306 reply("CSMSG_UNF_RESPONSE");
6310 static CHANSERV_FUNC(cmd_ping)
6314 char response[MAXLEN];
6315 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6316 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6317 irc_privmsg(cmd->parent->bot, channel->name, response);
6320 reply("CSMSG_PING_RESPONSE");
6324 static CHANSERV_FUNC(cmd_wut)
6328 char response[MAXLEN];
6329 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6330 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6331 irc_privmsg(cmd->parent->bot, channel->name, response);
6334 reply("CSMSG_WUT_RESPONSE");
6338 static CHANSERV_FUNC(cmd_8ball)
6340 unsigned int i, j, accum;
6345 for(i=1; i<argc; i++)
6346 for(j=0; argv[i][j]; j++)
6347 accum = (accum << 5) - accum + toupper(argv[i][j]);
6348 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6351 char response[MAXLEN];
6352 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6353 irc_privmsg(cmd->parent->bot, channel->name, response);
6356 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6360 static CHANSERV_FUNC(cmd_d)
6362 unsigned long sides, count, modifier, ii, total;
6363 char response[MAXLEN], *sep;
6367 if((count = strtoul(argv[1], &sep, 10)) < 1)
6377 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6378 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6382 else if((sep[0] == '-') && isdigit(sep[1]))
6383 modifier = strtoul(sep, NULL, 10);
6384 else if((sep[0] == '+') && isdigit(sep[1]))
6385 modifier = strtoul(sep+1, NULL, 10);
6392 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6397 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6400 for(total = ii = 0; ii < count; ++ii)
6401 total += (rand() % sides) + 1;
6404 if((count > 1) || modifier)
6406 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6407 sprintf(response, fmt, total, count, sides, modifier);
6411 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6412 sprintf(response, fmt, total, sides);
6415 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6417 send_message_type(4, user, cmd->parent->bot, "%s", response);
6421 static CHANSERV_FUNC(cmd_huggle)
6423 /* CTCP must be via PRIVMSG, never notice */
6425 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6427 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6432 chanserv_adjust_limit(void *data)
6434 struct mod_chanmode change;
6435 struct chanData *cData = data;
6436 struct chanNode *channel = cData->channel;
6439 if(IsSuspended(cData))
6442 cData->limitAdjusted = now;
6443 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6444 if(cData->modes.modes_set & MODE_LIMIT)
6446 if(limit > cData->modes.new_limit)
6447 limit = cData->modes.new_limit;
6448 else if(limit == cData->modes.new_limit)
6452 mod_chanmode_init(&change);
6453 change.modes_set = MODE_LIMIT;
6454 change.new_limit = limit;
6455 mod_chanmode_announce(chanserv, channel, &change);
6459 handle_new_channel(struct chanNode *channel)
6461 struct chanData *cData;
6463 if(!(cData = channel->channel_info))
6466 if(cData->modes.modes_set || cData->modes.modes_clear)
6467 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6469 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6470 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6473 /* Welcome to my worst nightmare. Warning: Read (or modify)
6474 the code below at your own risk. */
6476 handle_join(struct modeNode *mNode)
6478 struct mod_chanmode change;
6479 struct userNode *user = mNode->user;
6480 struct chanNode *channel = mNode->channel;
6481 struct chanData *cData;
6482 struct userData *uData = NULL;
6483 struct banData *bData;
6484 struct handle_info *handle;
6485 unsigned int modes = 0, info = 0;
6488 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6491 cData = channel->channel_info;
6492 if(channel->members.used > cData->max)
6493 cData->max = channel->members.used;
6495 /* Check for bans. If they're joining through a ban, one of two
6497 * 1: Join during a netburst, by riding the break. Kick them
6498 * unless they have ops or voice in the channel.
6499 * 2: They're allowed to join through the ban (an invite in
6500 * ircu2.10, or a +e on Hybrid, or something).
6501 * If they're not joining through a ban, and the banlist is not
6502 * full, see if they're on the banlist for the channel. If so,
6505 if(user->uplink->burst && !mNode->modes)
6508 for(ii = 0; ii < channel->banlist.used; ii++)
6510 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6512 /* Riding a netburst. Naughty. */
6513 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6519 mod_chanmode_init(&change);
6521 if(channel->banlist.used < MAXBANS)
6523 /* Not joining through a ban. */
6524 for(bData = cData->bans;
6525 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6526 bData = bData->next);
6530 char kick_reason[MAXLEN];
6531 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6533 bData->triggered = now;
6534 if(bData != cData->bans)
6536 /* Shuffle the ban to the head of the list. */
6538 bData->next->prev = bData->prev;
6540 bData->prev->next = bData->next;
6543 bData->next = cData->bans;
6546 cData->bans->prev = bData;
6547 cData->bans = bData;
6550 change.args[0].mode = MODE_BAN;
6551 change.args[0].u.hostmask = bData->mask;
6552 mod_chanmode_announce(chanserv, channel, &change);
6553 KickChannelUser(user, channel, chanserv, kick_reason);
6558 /* ChanServ will not modify the limits in join-flooded channels,
6559 or when there are enough slots left below the limit. */
6560 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6561 && !channel->join_flooded
6562 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6564 /* The user count has begun "bumping" into the channel limit,
6565 so set a timer to raise the limit a bit. Any previous
6566 timers are removed so three incoming users within the delay
6567 results in one limit change, not three. */
6569 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6570 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6573 if(channel->join_flooded)
6575 /* don't automatically give ops or voice during a join flood */
6577 else if(cData->lvlOpts[lvlGiveOps] == 0)
6578 modes |= MODE_CHANOP;
6579 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6580 modes |= MODE_VOICE;
6582 greeting = cData->greeting;
6583 if(user->handle_info)
6585 handle = user->handle_info;
6587 if(IsHelper(user) && !IsHelping(user))
6590 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6592 if(channel == chanserv_conf.support_channels.list[ii])
6594 HANDLE_SET_FLAG(user->handle_info, HELPING);
6600 uData = GetTrueChannelAccess(cData, handle);
6601 if(uData && !IsUserSuspended(uData))
6603 /* Ops and above were handled by the above case. */
6604 if(IsUserAutoOp(uData))
6606 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6607 modes |= MODE_CHANOP;
6608 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6609 modes |= MODE_VOICE;
6611 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6612 cData->visited = now;
6613 if(cData->user_greeting)
6614 greeting = cData->user_greeting;
6616 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6617 && ((now - uData->seen) >= chanserv_conf.info_delay)
6625 /* If user joining normally (not during burst), apply op or voice,
6626 * and send greeting/userinfo as appropriate.
6628 if(!user->uplink->burst)
6632 if(modes & MODE_CHANOP)
6633 modes &= ~MODE_VOICE;
6634 change.args[0].mode = modes;
6635 change.args[0].u.member = mNode;
6636 mod_chanmode_announce(chanserv, channel, &change);
6639 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6640 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
6641 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6647 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6649 struct mod_chanmode change;
6650 struct userData *channel;
6651 unsigned int ii, jj;
6653 if(!user->handle_info)
6656 mod_chanmode_init(&change);
6658 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6660 struct chanNode *cn;
6661 struct modeNode *mn;
6662 if(IsUserSuspended(channel)
6663 || IsSuspended(channel->channel)
6664 || !(cn = channel->channel->channel))
6667 mn = GetUserMode(cn, user);
6670 if(!IsUserSuspended(channel)
6671 && IsUserAutoInvite(channel)
6672 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6674 && !user->uplink->burst)
6675 irc_invite(chanserv, user, cn);
6679 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
6680 channel->channel->visited = now;
6682 if(IsUserAutoOp(channel))
6684 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6685 change.args[0].mode = MODE_CHANOP;
6686 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6687 change.args[0].mode = MODE_VOICE;
6689 change.args[0].mode = 0;
6690 change.args[0].u.member = mn;
6691 if(change.args[0].mode)
6692 mod_chanmode_announce(chanserv, cn, &change);
6695 channel->seen = now;
6696 channel->present = 1;
6699 for(ii = 0; ii < user->channels.used; ++ii)
6701 struct chanNode *chan = user->channels.list[ii]->channel;
6702 struct banData *ban;
6704 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6705 || !chan->channel_info
6706 || IsSuspended(chan->channel_info))
6708 for(jj = 0; jj < chan->banlist.used; ++jj)
6709 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6711 if(jj < chan->banlist.used)
6713 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6715 char kick_reason[MAXLEN];
6716 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6718 change.args[0].mode = MODE_BAN;
6719 change.args[0].u.hostmask = ban->mask;
6720 mod_chanmode_announce(chanserv, chan, &change);
6721 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6722 KickChannelUser(user, chan, chanserv, kick_reason);
6723 ban->triggered = now;
6728 if(IsSupportHelper(user))
6730 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6732 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6734 HANDLE_SET_FLAG(user->handle_info, HELPING);
6742 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6744 struct chanData *cData;
6745 struct userData *uData;
6747 cData = mn->channel->channel_info;
6748 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6751 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6753 /* Allow for a bit of padding so that the limit doesn't
6754 track the user count exactly, which could get annoying. */
6755 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6757 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6758 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6762 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6764 scan_user_presence(uData, mn->user);
6766 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6767 cData->visited = now;
6770 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6773 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6774 struct chanNode *channel;
6775 struct userNode *exclude;
6776 /* When looking at the channel that is being /part'ed, we
6777 * have to skip over the client that is leaving. For
6778 * other channels, we must not do that.
6780 channel = chanserv_conf.support_channels.list[ii];
6781 exclude = (channel == mn->channel) ? mn->user : NULL;
6782 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6785 if(ii == chanserv_conf.support_channels.used)
6786 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6791 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6793 struct userData *uData;
6795 if(!channel->channel_info || !kicker || IsService(kicker)
6796 || (kicker == victim) || IsSuspended(channel->channel_info)
6797 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6800 if(protect_user(victim, kicker, channel->channel_info))
6802 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6803 KickChannelUser(kicker, channel, chanserv, reason);
6806 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6811 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6813 struct chanData *cData;
6815 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6818 cData = channel->channel_info;
6819 if(bad_topic(channel, user, channel->topic))
6821 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6822 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6823 SetChannelTopic(channel, chanserv, old_topic, 1);
6824 else if(cData->topic)
6825 SetChannelTopic(channel, chanserv, cData->topic, 1);
6828 /* With topicsnarf, grab the topic and save it as the default topic. */
6829 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6832 cData->topic = strdup(channel->topic);
6838 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6840 struct mod_chanmode *bounce = NULL;
6841 unsigned int bnc, ii;
6844 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6847 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6848 && mode_lock_violated(&channel->channel_info->modes, change))
6850 char correct[MAXLEN];
6851 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6852 mod_chanmode_format(&channel->channel_info->modes, correct);
6853 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6855 for(ii = bnc = 0; ii < change->argc; ++ii)
6857 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6859 const struct userNode *victim = change->args[ii].u.member->user;
6860 if(!protect_user(victim, user, channel->channel_info))
6863 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6866 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6867 bounce->args[bnc].u.member = GetUserMode(channel, user);
6868 if(bounce->args[bnc].u.member)
6872 bounce->args[bnc].mode = MODE_CHANOP;
6873 bounce->args[bnc].u.member = change->args[ii].u.member;
6875 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6877 else if(change->args[ii].mode & MODE_CHANOP)
6879 const struct userNode *victim = change->args[ii].u.member->user;
6880 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6883 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6884 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6885 bounce->args[bnc].u.member = change->args[ii].u.member;
6888 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6890 const char *ban = change->args[ii].u.hostmask;
6891 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6894 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6895 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6896 bounce->args[bnc].u.hostmask = strdup(ban);
6898 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6903 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6904 mod_chanmode_announce(chanserv, channel, bounce);
6905 for(ii = 0; ii < change->argc; ++ii)
6906 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6907 free((char*)bounce->args[ii].u.hostmask);
6908 mod_chanmode_free(bounce);
6913 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6915 struct chanNode *channel;
6916 struct banData *bData;
6917 struct mod_chanmode change;
6918 unsigned int ii, jj;
6919 char kick_reason[MAXLEN];
6921 mod_chanmode_init(&change);
6923 change.args[0].mode = MODE_BAN;
6924 for(ii = 0; ii < user->channels.used; ++ii)
6926 channel = user->channels.list[ii]->channel;
6927 /* Need not check for bans if they're opped or voiced. */
6928 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6930 /* Need not check for bans unless channel registration is active. */
6931 if(!channel->channel_info || IsSuspended(channel->channel_info))
6933 /* Look for a matching ban already on the channel. */
6934 for(jj = 0; jj < channel->banlist.used; ++jj)
6935 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6937 /* Need not act if we found one. */
6938 if(jj < channel->banlist.used)
6940 /* Look for a matching ban in this channel. */
6941 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6943 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6945 change.args[0].u.hostmask = bData->mask;
6946 mod_chanmode_announce(chanserv, channel, &change);
6947 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6948 KickChannelUser(user, channel, chanserv, kick_reason);
6949 bData->triggered = now;
6950 break; /* we don't need to check any more bans in the channel */
6955 static void handle_rename(struct handle_info *handle, const char *old_handle)
6957 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6961 dict_remove2(handle_dnrs, old_handle, 1);
6962 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6963 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6968 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6970 struct userNode *h_user;
6972 if(handle->channels)
6974 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6975 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6977 while(handle->channels)
6978 del_channel_user(handle->channels, 1);
6983 handle_server_link(UNUSED_ARG(struct server *server))
6985 struct chanData *cData;
6987 for(cData = channelList; cData; cData = cData->next)
6989 if(!IsSuspended(cData))
6990 cData->may_opchan = 1;
6991 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6992 && !cData->channel->join_flooded
6993 && ((cData->channel->limit - cData->channel->members.used)
6994 < chanserv_conf.adjust_threshold))
6996 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6997 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7003 chanserv_conf_read(void)
7007 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7008 struct mod_chanmode *change;
7009 struct string_list *strlist;
7010 struct chanNode *chan;
7013 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7015 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7018 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7019 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7020 chanserv_conf.support_channels.used = 0;
7021 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7023 for(ii = 0; ii < strlist->used; ++ii)
7025 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7028 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7030 channelList_append(&chanserv_conf.support_channels, chan);
7033 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7036 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7039 chan = AddChannel(str, now, str2, NULL);
7041 channelList_append(&chanserv_conf.support_channels, chan);
7043 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7044 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7045 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7046 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7047 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7048 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7049 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7050 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7051 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7052 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7053 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7054 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7055 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7056 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7057 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7058 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7059 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7060 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7061 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7062 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7063 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7064 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7065 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7066 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7067 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7069 NickChange(chanserv, str, 0);
7070 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7071 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7072 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7073 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7074 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7075 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7076 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7077 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7078 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7079 chanserv_conf.max_owned = str ? atoi(str) : 5;
7080 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7081 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7082 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7083 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7084 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7085 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7086 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7089 safestrncpy(mode_line, str, sizeof(mode_line));
7090 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7091 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7092 && (change->argc < 2))
7094 chanserv_conf.default_modes = *change;
7095 mod_chanmode_free(change);
7097 free_string_list(chanserv_conf.set_shows);
7098 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7100 strlist = string_list_copy(strlist);
7103 static const char *list[] = {
7104 /* free form text */
7105 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7106 /* options based on user level */
7107 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7108 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7109 /* multiple choice options */
7110 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7111 /* binary options */
7112 "DynLimit", "NoDelete",
7116 strlist = alloc_string_list(ArrayLength(list)-1);
7117 for(ii=0; list[ii]; ii++)
7118 string_list_append(strlist, strdup(list[ii]));
7120 chanserv_conf.set_shows = strlist;
7121 /* We don't look things up now, in case the list refers to options
7122 * defined by modules initialized after this point. Just mark the
7123 * function list as invalid, so it will be initialized.
7125 set_shows_list.used = 0;
7126 free_string_list(chanserv_conf.eightball);
7127 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7130 strlist = string_list_copy(strlist);
7134 strlist = alloc_string_list(4);
7135 string_list_append(strlist, strdup("Yes."));
7136 string_list_append(strlist, strdup("No."));
7137 string_list_append(strlist, strdup("Maybe so."));
7139 chanserv_conf.eightball = strlist;
7140 free_string_list(chanserv_conf.old_ban_names);
7141 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7143 strlist = string_list_copy(strlist);
7145 strlist = alloc_string_list(2);
7146 chanserv_conf.old_ban_names = strlist;
7147 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7148 off_channel = str ? atoi(str) : 0;
7152 chanserv_note_type_read(const char *key, struct record_data *rd)
7155 struct note_type *ntype;
7158 if(!(obj = GET_RECORD_OBJECT(rd)))
7160 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7163 if(!(ntype = chanserv_create_note_type(key)))
7165 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7169 /* Figure out set access */
7170 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7172 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7173 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7175 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7177 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7178 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7180 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7182 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7186 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7187 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7188 ntype->set_access.min_opserv = 0;
7191 /* Figure out visibility */
7192 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7193 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7194 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7195 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7196 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7197 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7198 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7199 ntype->visible_type = NOTE_VIS_ALL;
7201 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7203 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7204 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7208 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7210 struct handle_info *handle;
7211 struct userData *uData;
7212 char *seen, *inf, *flags;
7213 unsigned long last_seen;
7214 unsigned short access_level;
7216 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7218 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7222 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7223 if(access_level > UL_OWNER)
7225 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7229 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7230 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7231 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7232 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7233 handle = get_handle_info(key);
7236 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7240 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7241 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7245 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7247 struct banData *bData;
7248 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7249 unsigned long set_time, triggered_time, expires_time;
7251 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7253 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7257 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7258 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7259 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7260 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7261 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7262 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7263 if (!reason || !owner)
7266 set_time = set ? strtoul(set, NULL, 0) : now;
7267 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7269 expires_time = strtoul(s_expires, NULL, 0);
7271 expires_time = set_time + atoi(s_duration);
7275 if(!reason || (expires_time && (expires_time < now)))
7278 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7281 static struct suspended *
7282 chanserv_read_suspended(dict_t obj)
7284 struct suspended *suspended = calloc(1, sizeof(*suspended));
7288 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7289 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7290 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7291 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7292 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7293 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7294 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7295 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7296 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7297 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7302 chanserv_channel_read(const char *key, struct record_data *hir)
7304 struct suspended *suspended;
7305 struct mod_chanmode *modes;
7306 struct chanNode *cNode;
7307 struct chanData *cData;
7308 struct dict *channel, *obj;
7309 char *str, *argv[10];
7313 channel = hir->d.object;
7315 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7318 cNode = AddChannel(key, now, NULL, NULL);
7321 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7324 cData = register_channel(cNode, str);
7327 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7331 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7333 enum levelOption lvlOpt;
7334 enum charOption chOpt;
7336 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7337 cData->flags = atoi(str);
7339 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7341 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7343 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7344 else if(levelOptions[lvlOpt].old_flag)
7346 if(cData->flags & levelOptions[lvlOpt].old_flag)
7347 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7349 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7353 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7355 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7357 cData->chOpts[chOpt] = str[0];
7360 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7362 enum levelOption lvlOpt;
7363 enum charOption chOpt;
7366 cData->flags = base64toint(str, 5);
7367 count = strlen(str += 5);
7368 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7371 if(levelOptions[lvlOpt].old_flag)
7373 if(cData->flags & levelOptions[lvlOpt].old_flag)
7374 lvl = levelOptions[lvlOpt].flag_value;
7376 lvl = levelOptions[lvlOpt].default_value;
7378 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7380 case 'c': lvl = UL_COOWNER; break;
7381 case 'm': lvl = UL_MASTER; break;
7382 case 'n': lvl = UL_OWNER+1; break;
7383 case 'o': lvl = UL_OP; break;
7384 case 'p': lvl = UL_PEON; break;
7385 case 'w': lvl = UL_OWNER; break;
7386 default: lvl = 0; break;
7388 cData->lvlOpts[lvlOpt] = lvl;
7390 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7391 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7394 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7396 suspended = chanserv_read_suspended(obj);
7397 cData->suspended = suspended;
7398 suspended->cData = cData;
7399 /* We could use suspended->expires and suspended->revoked to
7400 * set the CHANNEL_SUSPENDED flag, but we don't. */
7402 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7404 suspended = calloc(1, sizeof(*suspended));
7405 suspended->issued = 0;
7406 suspended->revoked = 0;
7407 suspended->suspender = strdup(str);
7408 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7409 suspended->expires = str ? atoi(str) : 0;
7410 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7411 suspended->reason = strdup(str ? str : "No reason");
7412 suspended->previous = NULL;
7413 cData->suspended = suspended;
7414 suspended->cData = cData;
7418 cData->flags &= ~CHANNEL_SUSPENDED;
7419 suspended = NULL; /* to squelch a warning */
7422 if(IsSuspended(cData)) {
7423 if(suspended->expires > now)
7424 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7425 else if(suspended->expires)
7426 cData->flags &= ~CHANNEL_SUSPENDED;
7429 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7430 struct mod_chanmode change;
7431 mod_chanmode_init(&change);
7433 change.args[0].mode = MODE_CHANOP;
7434 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7435 mod_chanmode_announce(chanserv, cNode, &change);
7438 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7439 cData->registered = str ? strtoul(str, NULL, 0) : now;
7440 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7441 cData->visited = str ? strtoul(str, NULL, 0) : now;
7442 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7443 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7444 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7445 cData->max = str ? atoi(str) : 0;
7446 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7447 cData->greeting = str ? strdup(str) : NULL;
7448 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7449 cData->user_greeting = str ? strdup(str) : NULL;
7450 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7451 cData->topic_mask = str ? strdup(str) : NULL;
7452 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7453 cData->topic = str ? strdup(str) : NULL;
7455 if(!IsSuspended(cData)
7456 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7457 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7458 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
7459 cData->modes = *modes;
7461 cData->modes.modes_set |= MODE_REGISTERED;
7462 if(cData->modes.argc > 1)
7463 cData->modes.argc = 1;
7464 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7465 mod_chanmode_free(modes);
7468 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7469 for(it = dict_first(obj); it; it = iter_next(it))
7470 user_read_helper(iter_key(it), iter_data(it), cData);
7472 if(!cData->users && !IsProtected(cData))
7474 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7475 unregister_channel(cData, "has empty user list.");
7479 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7480 for(it = dict_first(obj); it; it = iter_next(it))
7481 ban_read_helper(iter_key(it), iter_data(it), cData);
7483 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7484 for(it = dict_first(obj); it; it = iter_next(it))
7486 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7487 struct record_data *rd = iter_data(it);
7488 const char *note, *setter;
7490 if(rd->type != RECDB_OBJECT)
7492 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7496 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7498 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7500 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7504 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7505 if(!setter) setter = "<unknown>";
7506 chanserv_add_channel_note(cData, ntype, setter, note);
7514 chanserv_dnr_read(const char *key, struct record_data *hir)
7516 const char *setter, *reason, *str;
7517 struct do_not_register *dnr;
7518 unsigned long expiry;
7520 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7523 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7526 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7529 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7532 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7533 expiry = str ? strtoul(str, NULL, 0) : 0;
7534 if(expiry && expiry <= now)
7536 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7539 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7541 dnr->set = atoi(str);
7547 chanserv_saxdb_read(struct dict *database)
7549 struct dict *section;
7552 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7553 for(it = dict_first(section); it; it = iter_next(it))
7554 chanserv_note_type_read(iter_key(it), iter_data(it));
7556 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7557 for(it = dict_first(section); it; it = iter_next(it))
7558 chanserv_channel_read(iter_key(it), iter_data(it));
7560 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7561 for(it = dict_first(section); it; it = iter_next(it))
7562 chanserv_dnr_read(iter_key(it), iter_data(it));
7568 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7570 int high_present = 0;
7571 saxdb_start_record(ctx, KEY_USERS, 1);
7572 for(; uData; uData = uData->next)
7574 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
7576 saxdb_start_record(ctx, uData->handle->handle, 0);
7577 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7578 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7580 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7582 saxdb_write_string(ctx, KEY_INFO, uData->info);
7583 saxdb_end_record(ctx);
7585 saxdb_end_record(ctx);
7586 return high_present;
7590 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7594 saxdb_start_record(ctx, KEY_BANS, 1);
7595 for(; bData; bData = bData->next)
7597 saxdb_start_record(ctx, bData->mask, 0);
7598 saxdb_write_int(ctx, KEY_SET, bData->set);
7599 if(bData->triggered)
7600 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7602 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7604 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7606 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7607 saxdb_end_record(ctx);
7609 saxdb_end_record(ctx);
7613 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7615 saxdb_start_record(ctx, name, 0);
7616 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7617 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7619 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7621 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7623 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7625 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7626 saxdb_end_record(ctx);
7630 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7634 enum levelOption lvlOpt;
7635 enum charOption chOpt;
7637 saxdb_start_record(ctx, channel->channel->name, 1);
7639 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7640 saxdb_write_int(ctx, KEY_MAX, channel->max);
7642 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7643 if(channel->registrar)
7644 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7645 if(channel->greeting)
7646 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7647 if(channel->user_greeting)
7648 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7649 if(channel->topic_mask)
7650 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7651 if(channel->suspended)
7652 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7654 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7655 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7656 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7657 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7658 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7660 buf[0] = channel->chOpts[chOpt];
7662 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7664 saxdb_end_record(ctx);
7666 if(channel->modes.modes_set || channel->modes.modes_clear)
7668 mod_chanmode_format(&channel->modes, buf);
7669 saxdb_write_string(ctx, KEY_MODES, buf);
7672 high_present = chanserv_write_users(ctx, channel->users);
7673 chanserv_write_bans(ctx, channel->bans);
7675 if(dict_size(channel->notes))
7679 saxdb_start_record(ctx, KEY_NOTES, 1);
7680 for(it = dict_first(channel->notes); it; it = iter_next(it))
7682 struct note *note = iter_data(it);
7683 saxdb_start_record(ctx, iter_key(it), 0);
7684 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7685 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7686 saxdb_end_record(ctx);
7688 saxdb_end_record(ctx);
7691 if(channel->ownerTransfer)
7692 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7693 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7694 saxdb_end_record(ctx);
7698 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7702 saxdb_start_record(ctx, ntype->name, 0);
7703 switch(ntype->set_access_type)
7705 case NOTE_SET_CHANNEL_ACCESS:
7706 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7708 case NOTE_SET_CHANNEL_SETTER:
7709 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7711 case NOTE_SET_PRIVILEGED: default:
7712 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7715 switch(ntype->visible_type)
7717 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7718 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7719 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7721 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7722 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7723 saxdb_end_record(ctx);
7727 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7729 struct do_not_register *dnr;
7730 dict_iterator_t it, next;
7732 for(it = dict_first(dnrs); it; it = next)
7734 next = iter_next(it);
7735 dnr = iter_data(it);
7736 if(dnr->expires && dnr->expires <= now)
7738 dict_remove(dnrs, iter_key(it));
7741 saxdb_start_record(ctx, dnr->chan_name, 0);
7743 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7745 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7746 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7747 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7748 saxdb_end_record(ctx);
7753 chanserv_saxdb_write(struct saxdb_context *ctx)
7756 struct chanData *channel;
7759 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7760 for(it = dict_first(note_types); it; it = iter_next(it))
7761 chanserv_write_note_type(ctx, iter_data(it));
7762 saxdb_end_record(ctx);
7765 saxdb_start_record(ctx, KEY_DNR, 1);
7766 write_dnrs_helper(ctx, handle_dnrs);
7767 write_dnrs_helper(ctx, plain_dnrs);
7768 write_dnrs_helper(ctx, mask_dnrs);
7769 saxdb_end_record(ctx);
7772 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7773 for(channel = channelList; channel; channel = channel->next)
7774 chanserv_write_channel(ctx, channel);
7775 saxdb_end_record(ctx);
7781 chanserv_db_cleanup(void) {
7783 unreg_part_func(handle_part);
7785 unregister_channel(channelList, "terminating.");
7786 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7787 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7788 free(chanserv_conf.support_channels.list);
7789 dict_delete(handle_dnrs);
7790 dict_delete(plain_dnrs);
7791 dict_delete(mask_dnrs);
7792 dict_delete(note_types);
7793 free_string_list(chanserv_conf.eightball);
7794 free_string_list(chanserv_conf.old_ban_names);
7795 free_string_list(chanserv_conf.set_shows);
7796 free(set_shows_list.list);
7797 free(uset_shows_list.list);
7800 struct userData *helper = helperList;
7801 helperList = helperList->next;
7806 #if defined(GCC_VARMACROS)
7807 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7808 #elif defined(C99_VARMACROS)
7809 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7811 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7812 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7815 init_chanserv(const char *nick)
7817 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7818 conf_register_reload(chanserv_conf_read);
7822 reg_server_link_func(handle_server_link);
7823 reg_new_channel_func(handle_new_channel);
7824 reg_join_func(handle_join);
7825 reg_part_func(handle_part);
7826 reg_kick_func(handle_kick);
7827 reg_topic_func(handle_topic);
7828 reg_mode_change_func(handle_mode);
7829 reg_nick_change_func(handle_nick_change);
7830 reg_auth_func(handle_auth);
7833 reg_handle_rename_func(handle_rename);
7834 reg_unreg_func(handle_unreg);
7836 handle_dnrs = dict_new();
7837 dict_set_free_data(handle_dnrs, free);
7838 plain_dnrs = dict_new();
7839 dict_set_free_data(plain_dnrs, free);
7840 mask_dnrs = dict_new();
7841 dict_set_free_data(mask_dnrs, free);
7843 reg_svccmd_unbind_func(handle_svccmd_unbind);
7844 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7845 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7846 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7847 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7848 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7849 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7850 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7851 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7852 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7853 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7854 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7855 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7856 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7858 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7859 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7861 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7862 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7863 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7864 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7865 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7867 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7868 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7869 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7870 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7871 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7873 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7874 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7875 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7876 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7878 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7879 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7880 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7881 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7882 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7883 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7884 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7885 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7887 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7888 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7889 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7890 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7891 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7892 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7893 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7894 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7895 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7896 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7897 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7898 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7899 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7900 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7902 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7903 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7904 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7905 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7906 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7908 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7909 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7911 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7912 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7913 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7914 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7915 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7916 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7917 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7918 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7919 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7920 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7921 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7923 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7924 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7926 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7927 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7928 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7929 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7931 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7932 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7933 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7934 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7935 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7937 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7938 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7939 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7940 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7941 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7942 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7944 /* Channel options */
7945 DEFINE_CHANNEL_OPTION(defaulttopic);
7946 DEFINE_CHANNEL_OPTION(topicmask);
7947 DEFINE_CHANNEL_OPTION(greeting);
7948 DEFINE_CHANNEL_OPTION(usergreeting);
7949 DEFINE_CHANNEL_OPTION(modes);
7950 DEFINE_CHANNEL_OPTION(enfops);
7951 DEFINE_CHANNEL_OPTION(giveops);
7952 DEFINE_CHANNEL_OPTION(protect);
7953 DEFINE_CHANNEL_OPTION(enfmodes);
7954 DEFINE_CHANNEL_OPTION(enftopic);
7955 DEFINE_CHANNEL_OPTION(pubcmd);
7956 DEFINE_CHANNEL_OPTION(givevoice);
7957 DEFINE_CHANNEL_OPTION(userinfo);
7958 DEFINE_CHANNEL_OPTION(dynlimit);
7959 DEFINE_CHANNEL_OPTION(topicsnarf);
7960 DEFINE_CHANNEL_OPTION(nodelete);
7961 DEFINE_CHANNEL_OPTION(toys);
7962 DEFINE_CHANNEL_OPTION(setters);
7963 DEFINE_CHANNEL_OPTION(topicrefresh);
7964 DEFINE_CHANNEL_OPTION(ctcpusers);
7965 DEFINE_CHANNEL_OPTION(ctcpreaction);
7966 DEFINE_CHANNEL_OPTION(inviteme);
7967 DEFINE_CHANNEL_OPTION(unreviewed);
7968 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7969 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7971 DEFINE_CHANNEL_OPTION(offchannel);
7972 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7974 /* Alias set topic to set defaulttopic for compatibility. */
7975 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7978 DEFINE_USER_OPTION(noautoop);
7979 DEFINE_USER_OPTION(autoinvite);
7980 DEFINE_USER_OPTION(info);
7982 /* Alias uset autovoice to uset autoop. */
7983 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7985 note_types = dict_new();
7986 dict_set_free_data(note_types, chanserv_deref_note_type);
7989 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7990 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7991 service_register(chanserv)->trigger = '!';
7992 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7994 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7996 if(chanserv_conf.channel_expire_frequency)
7997 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7999 if(chanserv_conf.dnr_expire_frequency)
8000 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8002 if(chanserv_conf.refresh_period)
8004 unsigned long next_refresh;
8005 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8006 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8009 reg_exit_func(chanserv_db_cleanup);
8010 message_register_table(msgtab);