1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 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 3 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.
27 #include "opserv.h" /* for opserv_bad_channel() */
28 #include "nickserv.h" /* for oper_outranks() */
34 #define CHANSERV_CONF_NAME "services/chanserv"
36 /* ChanServ options */
37 #define KEY_SUPPORT_CHANNEL "support_channel"
38 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
39 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
40 #define KEY_INFO_DELAY "info_delay"
41 #define KEY_MAX_GREETLEN "max_greetlen"
42 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
43 #define KEY_ADJUST_DELAY "adjust_delay"
44 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
45 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
46 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
47 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
48 #define KEY_MAX_CHAN_USERS "max_chan_users"
49 #define KEY_MAX_CHAN_BANS "max_chan_bans"
50 #define KEY_NICK "nick"
51 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
52 #define KEY_8BALL_RESPONSES "8ball"
53 #define KEY_OLD_BAN_NAMES "old_ban_names"
54 #define KEY_REFRESH_PERIOD "refresh_period"
55 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
56 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
57 #define KEY_MAX_OWNED "max_owned"
58 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
59 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
60 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
61 #define KEY_NODELETE_LEVEL "nodelete_level"
62 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
63 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
64 #define KEY_VALID_CHANNEL_REGEX "valid_channel_regex"
66 /* ChanServ database */
67 #define KEY_VERSION_CONTROL "version_control"
68 #define KEY_CHANNELS "channels"
69 #define KEY_NOTE_TYPES "note_types"
71 /* version control paramiter */
72 #define KEY_VERSION_NUMBER "version_number"
74 /* Note type parameters */
75 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
76 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
77 #define KEY_NOTE_SETTER_ACCESS "setter_access"
78 #define KEY_NOTE_VISIBILITY "visibility"
79 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
80 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
81 #define KEY_NOTE_VIS_ALL "all"
82 #define KEY_NOTE_MAX_LENGTH "max_length"
83 #define KEY_NOTE_SETTER "setter"
84 #define KEY_NOTE_NOTE "note"
86 /* Do-not-register channels */
88 #define KEY_DNR_SET "set"
89 #define KEY_DNR_SETTER "setter"
90 #define KEY_DNR_REASON "reason"
93 #define KEY_REGISTERED "registered"
94 #define KEY_REGISTRAR "registrar"
95 #define KEY_SUSPENDED "suspended"
96 #define KEY_PREVIOUS "previous"
97 #define KEY_SUSPENDER "suspender"
98 #define KEY_ISSUED "issued"
99 #define KEY_REVOKED "revoked"
100 #define KEY_SUSPEND_EXPIRES "suspend_expires"
101 #define KEY_SUSPEND_REASON "suspend_reason"
102 #define KEY_GIVEOWNERSHIP "giveownership"
103 #define KEY_STAFF_ISSUER "staff_issuer"
104 #define KEY_OLD_OWNER "old_owner"
105 #define KEY_TARGET "target"
106 #define KEY_TARGET_ACCESS "target_access"
107 #define KEY_VISITED "visited"
108 #define KEY_TOPIC "topic"
109 #define KEY_GREETING "greeting"
110 #define KEY_USER_GREETING "user_greeting"
111 #define KEY_MODES "modes"
112 #define KEY_FLAGS "flags"
113 #define KEY_OPTIONS "options"
114 #define KEY_USERS "users"
115 #define KEY_BANS "bans" /* for lamers */
116 #define KEY_MAX "max"
117 #define KEY_NOTES "notes"
118 #define KEY_TOPIC_MASK "topic_mask"
119 #define KEY_OWNER_TRANSFER "owner_transfer"
120 #define KEY_MAXSETINFO "maxsetinfo"
123 #define KEY_LEVEL "level"
124 #define KEY_INFO "info"
125 #define KEY_SEEN "seen"
126 #define KEY_ACCESSEXPIRY "accessexpiry"
127 #define KEY_CLVLEXPIRY "clvlexpiry"
128 #define KEY_LASTLEVEL "lastlevel"
131 #define KEY_OWNER "owner"
132 #define KEY_REASON "reason"
133 #define KEY_SET "set"
134 #define KEY_DURATION "duration"
135 #define KEY_EXPIRES "expires"
136 #define KEY_TRIGGERED "triggered"
138 #define KEY_GOD_TIMEOUT "god_timeout"
140 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
141 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
142 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
144 /* Administrative messages */
145 static const struct message_entry msgtab
[] = {
146 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
148 /* Channel registration */
149 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
150 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
151 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
152 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
153 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
154 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
155 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
156 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
158 /* Do-not-register channels */
159 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
160 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
161 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
162 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
163 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
164 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
165 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
166 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
167 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
168 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
169 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
170 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
171 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
172 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
174 /* Channel unregistration */
175 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
176 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
177 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
178 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
181 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
182 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
184 /* Channel merging */
185 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
186 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
187 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
188 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
189 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
191 /* Handle unregistration */
192 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
195 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
196 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
197 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
198 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
199 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
200 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
201 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
202 { "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." },
203 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
204 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
205 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
206 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
207 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
208 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
209 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
210 { "CSMSG_NOT_IN_CHANNEL", "I am not in %s." },
212 /* Removing yourself from a channel. */
213 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
214 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
215 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
217 /* User management */
218 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
219 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
220 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
221 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
222 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
223 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
224 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
225 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
226 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
227 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
228 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
229 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
230 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
231 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
232 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
233 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
234 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
236 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
237 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
238 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
239 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
240 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
241 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
242 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
243 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
244 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
247 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
248 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
249 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
250 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
251 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
252 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
253 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
254 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
255 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
256 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
257 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
258 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
259 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
260 { "CSMSG_NO_EXTBANS", "$b%s$b is an extended ban, which are not allowed." },
261 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
262 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
263 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
264 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
265 { "CSMSG_BAD_BAN", "The given ban $b%s$b is invalid." },
267 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
269 /* Channel management */
270 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
271 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
272 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
274 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
275 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
276 { "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" },
277 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
278 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
279 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
280 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
282 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
283 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
284 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
285 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
286 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
287 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
288 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
289 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
290 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
291 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
292 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
293 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
294 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
295 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
296 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
297 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
298 { "CSMSG_SET_MODES", "$bModes $b %s" },
299 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
300 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
301 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
302 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
303 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
304 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
305 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
306 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
307 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
308 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
309 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
310 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
311 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
312 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
313 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
314 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
315 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
316 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
317 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
318 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
319 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
321 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
322 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
323 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
324 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
325 { "CSMSG_USET_INFO", "$bInfo $b %s" },
327 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
328 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
329 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
330 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
331 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
332 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
333 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
334 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
335 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
336 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
337 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
339 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
340 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
341 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
342 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
343 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
344 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
345 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
347 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
348 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
349 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
350 { "CSMSG_PROTECT_NONE", "No users will be protected." },
351 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
352 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
353 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
355 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
356 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
357 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
358 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
359 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
361 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
362 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
363 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
364 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
365 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
367 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
368 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
369 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
370 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
371 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
373 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
374 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
375 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
376 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
377 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
378 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
380 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
381 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
382 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
383 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
384 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
385 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
386 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
387 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
388 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
390 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
391 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
392 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
394 /* Channel userlist */
395 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
396 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
397 /* uncomment if needed to adujust styles (and change code below)
398 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
399 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
400 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
401 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
402 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
403 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
405 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
406 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
407 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
409 /* Channel note list */
410 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
411 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
412 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
413 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
414 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
415 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
416 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
417 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
418 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
419 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
420 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
421 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
422 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
423 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
424 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
426 /* Channel [un]suspension */
427 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
428 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
429 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
430 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
431 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
432 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
433 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
435 /* Access information */
436 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
437 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
438 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
439 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
440 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
441 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
442 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
443 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
444 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
445 { "CSMSG_SMURF_TARGET", "%s %s ($b%s$b)." },
446 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
447 { "CSMSG_UC_H_TITLE", "network helper" },
448 { "CSMSG_LC_H_TITLE", "support helper" },
449 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
451 /* Seen information */
452 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
453 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
454 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
455 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
457 /* Names information */
458 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
459 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
461 /* Channel information */
462 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
463 { "CSMSG_BAR", "----------------------------------------"},
464 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
465 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
466 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
467 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
468 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
469 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
470 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
471 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
472 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
473 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
474 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
475 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
476 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
477 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
478 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
479 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
480 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
481 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
482 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
483 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
484 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
485 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
486 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
487 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
488 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
489 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
491 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
492 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
493 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
494 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
495 { "CSMSG_PEEK_OPS", "$bOps:$b" },
496 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
497 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
499 /* Network information */
500 { "CSMSG_NETWORK_INFO", "Network Information:" },
501 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
502 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
503 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
504 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
505 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
506 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
507 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
508 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
511 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
512 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
513 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
515 /* Channel searches */
516 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
517 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
518 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
519 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
521 /* Channel configuration */
522 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
523 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
524 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
525 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
526 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
529 { "CSMSG_USER_OPTIONS", "User Options:" },
530 // { "CSMSG_USER_PROTECTED", "That user is protected." },
533 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
534 { "CSMSG_PING_RESPONSE", "Pong!" },
535 { "CSMSG_WUT_RESPONSE", "wut" },
536 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
537 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
538 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
539 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
540 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
541 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
542 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
543 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
544 { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
545 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
546 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
547 { "CSMSG_ROULETTE_CLICK", "Click" } ,
549 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
550 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
551 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
553 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
554 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
555 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
556 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
557 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
558 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
559 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
560 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
561 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
562 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
563 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
564 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
565 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
566 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
569 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
570 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
571 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
572 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
576 /* eject_user and unban_user flags */
577 #define ACTION_KICK 0x0001
578 #define ACTION_BAN 0x0002
579 #define ACTION_ADD_LAMER 0x0004
580 #define ACTION_ADD_TIMED_LAMER 0x0008
581 #define ACTION_UNBAN 0x0010
582 #define ACTION_DEL_LAMER 0x0020
584 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
585 #define MODELEN 40 + KEYLEN
589 #define CSFUNC_ARGS user, channel, argc, argv, cmd
591 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
592 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
593 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
594 reply("MSG_MISSING_PARAMS", argv[0]); \
598 DECLARE_LIST(dnrList
, struct do_not_register
*);
599 DEFINE_LIST(dnrList
, struct do_not_register
*)
601 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
603 struct userNode
*chanserv
;
606 extern struct string_list
*autojoin_channels
;
607 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
608 static struct log_type
*CS_LOG
;
609 struct adduserPending
* adduser_pendings
= NULL
;
610 unsigned int adduser_pendings_count
= 0;
611 unsigned long god_timeout
;
615 struct channelList support_channels
;
616 struct mod_chanmode default_modes
;
618 unsigned long db_backup_frequency
;
619 unsigned long channel_expire_frequency
;
620 unsigned long ban_timeout_frequency
;
621 unsigned long dnr_expire_frequency
;
624 unsigned int adjust_delay
;
625 long channel_expire_delay
;
626 unsigned int nodelete_level
;
628 unsigned int adjust_threshold
;
629 int join_flood_threshold
;
631 unsigned int greeting_length
;
632 unsigned int refresh_period
;
633 unsigned int giveownership_period
;
635 unsigned int max_owned
;
636 unsigned int max_chan_users
;
637 unsigned int max_chan_bans
; /* lamers */
638 unsigned int max_userinfo_length
;
639 unsigned int valid_channel_regex_set
: 1;
641 regex_t valid_channel_regex
;
643 struct string_list
*set_shows
;
644 struct string_list
*eightball
;
645 struct string_list
*old_ban_names
;
646 struct string_list
*wheel
;
648 const char *ctcp_short_ban_duration
;
649 const char *ctcp_long_ban_duration
;
651 const char *irc_operator_epithet
;
652 const char *network_helper_epithet
;
653 const char *support_helper_epithet
;
658 struct userNode
*user
;
659 struct userNode
*bot
;
660 struct chanNode
*channel
;
662 unsigned short lowest
;
663 unsigned short highest
;
664 struct userData
**users
;
665 struct helpfile_table table
;
668 enum note_access_type
670 NOTE_SET_CHANNEL_ACCESS
,
671 NOTE_SET_CHANNEL_SETTER
,
675 enum note_visible_type
678 NOTE_VIS_CHANNEL_USERS
,
682 struct io_fd
*socket_io_fd
;
683 extern struct cManagerNode cManager
;
687 enum note_access_type set_access_type
;
689 unsigned int min_opserv
;
690 unsigned short min_ulevel
;
692 enum note_visible_type visible_type
;
693 unsigned int max_length
;
700 struct note_type
*type
;
701 char setter
[NICKSERV_HANDLE_LEN
+1];
705 static unsigned int registered_channels
;
706 static unsigned int banCount
;
708 static const struct {
711 unsigned short level
;
713 } accessLevels
[] = { /* MUST be orderd less to most! */
714 { "pal", "Pal", UL_PEON
, '+' },
715 { "peon", "Peon", UL_PEON
, '+' },
716 { "halfop", "HalfOp", UL_HALFOP
, '%' },
717 { "op", "Op", UL_OP
, '@' },
718 { "manager", "Manager", UL_MANAGER
, '%' },
719 { "coowner", "Coowner", UL_COOWNER
, '*' },
720 { "owner", "Owner", UL_OWNER
, '!' },
721 { "helper", "BUG:", UL_HELPER
, 'X' }
724 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
725 static const struct {
728 unsigned short default_value
;
729 unsigned int old_idx
;
730 unsigned int old_flag
;
731 unsigned short flag_value
;
733 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
734 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
735 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
736 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
737 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
738 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
739 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
740 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
741 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
744 struct charOptionValues
{
747 } automodeValues
[] = {
748 { 'n', "CSMSG_AUTOMODE_NONE" },
749 { 'y', "CSMSG_AUTOMODE_NORMAL" },
750 { 'v', "CSMSG_AUTOMODE_VOICE" },
751 { 'h', "CSMSG_AUTOMODE_HOP" },
752 { 'o', "CSMSG_AUTOMODE_OP" },
753 { 'm', "CSMSG_AUTOMODE_MUTE" },
754 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
755 }, protectValues
[] = {
756 { 'a', "CSMSG_PROTECT_ALL" },
757 { 'e', "CSMSG_PROTECT_EQUAL" },
758 { 'l', "CSMSG_PROTECT_LOWER" },
759 { 'n', "CSMSG_PROTECT_NONE" }
761 { 'd', "CSMSG_TOYS_DISABLED" },
762 { 'n', "CSMSG_TOYS_PRIVATE" },
763 { 'p', "CSMSG_TOYS_PUBLIC" }
764 }, topicRefreshValues
[] = {
765 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
766 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
767 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
768 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
769 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
770 }, ctcpReactionValues
[] = {
771 { 'n', "CSMSG_CTCPREACTION_NONE" },
772 { 'k', "CSMSG_CTCPREACTION_KICK" },
773 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
774 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
775 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
776 }, banTimeoutValues
[] = {
777 { '0', "CSMSG_BANTIMEOUT_NONE" },
778 { '1', "CSMSG_BANTIMEOUT_10M" },
779 { '2', "CSMSG_BANTIMEOUT_2H" },
780 { '3', "CSMSG_BANTIMEOUT_4H" },
781 { '4', "CSMSG_BANTIMEOUT_1D" },
782 { '5', "CSMSG_BANTIMEOUT_1W" }
785 { 'n', "CSMSG_RESYNC_NEVER" },
786 { '1', "CSMSG_RESYNC_3_HOURS" },
787 { '2', "CSMSG_RESYNC_6_HOURS" },
788 { '3', "CSMSG_RESYNC_12_HOURS" },
789 { '4', "CSMSG_RESYNC_24_HOURS" }
792 static const struct {
796 unsigned int old_idx
;
798 struct charOptionValues
*values
;
800 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
801 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
802 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
803 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
804 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
805 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
806 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
809 struct userData
*helperList
;
810 struct chanData
*channelList
;
811 static struct module *chanserv_module
;
812 static unsigned int userCount
;
813 unsigned int chanserv_read_version
= 0; /* db version control */
815 #define CHANSERV_DB_VERSION 2
817 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
818 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
820 void sputsock(const char *text
, ...) PRINTF_LIKE(1, 2);
823 sputsock(const char *text
, ...)
829 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
831 va_start(arg_list
, text
);
832 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
834 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
836 log_replay(MAIN_LOG
, true, buffer
);
837 buffer
[pos
++] = '\n';
839 ioset_write(socket_io_fd
, buffer
, pos
);
843 user_level_from_name(const char *name
, unsigned short clamp_level
)
845 unsigned int level
= 0, ii
;
847 level
= strtoul(name
, NULL
, 10);
848 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
849 if(!irccasecmp(name
, accessLevels
[ii
].name
))
850 level
= accessLevels
[ii
].level
;
851 if(level
> clamp_level
)
857 user_level_name_from_level(int level
)
865 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
866 if(level
>= accessLevels
[ii
].level
)
867 highest
= accessLevels
[ii
].title
;
873 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
876 *minl
= strtoul(arg
, &sep
, 10);
884 *maxl
= strtoul(sep
+1, &sep
, 10);
892 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
894 struct userData
*uData
, **head
;
896 if(!channel
|| !handle
)
899 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
900 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
902 for(uData
= helperList
;
903 uData
&& uData
->handle
!= handle
;
904 uData
= uData
->next
);
908 uData
= calloc(1, sizeof(struct userData
));
909 uData
->handle
= handle
;
911 uData
->access
= UL_HELPER
;
917 uData
->next
= helperList
;
919 helperList
->prev
= uData
;
927 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
928 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
931 head
= &(channel
->users
);
934 if(uData
&& (uData
!= *head
))
936 /* Shuffle the user to the head of whatever list he was in. */
938 uData
->next
->prev
= uData
->prev
;
940 uData
->prev
->next
= uData
->next
;
946 (**head
).prev
= uData
;
953 /* Returns non-zero if user has at least the minimum access.
954 * exempt_owner is set when handling !set, so the owner can set things
957 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
959 struct userData
*uData
;
960 struct chanData
*cData
= channel
->channel_info
;
961 unsigned short minimum
= cData
->lvlOpts
[opt
];
964 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
967 if(minimum
<= uData
->access
)
969 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
974 /* Scan for other users authenticated to the same handle
975 still in the channel. If so, keep them listed as present.
977 user is optional, if not null, it skips checking that userNode
978 (for the handle_part function) */
980 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
984 if(IsSuspended(uData
->channel
)
985 || IsUserSuspended(uData
)
986 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
998 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, const char *text
, UNUSED_ARG(struct userNode
*bot
), UNUSED_ARG(unsigned int is_notice
), UNUSED_ARG(void *extra
))
1000 unsigned int eflags
, argc
;
1002 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
1004 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
1005 if(!channel
->channel_info
1006 || IsSuspended(channel
->channel_info
)
1008 || !ircncasecmp(text
, "ACTION ", 7))
1010 /* We dont punish people we know -Rubin
1011 * * Figure out the minimum level needed to CTCP the channel *
1013 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
1016 /* If they are a user of the channel, they are exempt */
1017 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
1019 /* We need to enforce against them; do so. */
1021 argv
[0] = (char*)text
;
1022 argv
[1] = user
->nick
;
1024 if(GetUserMode(channel
, user
))
1025 eflags
|= ACTION_KICK
;
1026 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
1027 default: case 'n': return;
1029 eflags
|= ACTION_KICK
;
1032 eflags
|= ACTION_BAN
;
1035 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1036 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
1039 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1040 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
1043 argv
[argc
++] = bad_ctcp_reason
;
1044 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
1048 chanserv_create_note_type(const char *name
)
1050 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
1051 strcpy(ntype
->name
, name
);
1053 dict_insert(note_types
, ntype
->name
, ntype
);
1058 chanserv_deref_note_type(void *data
)
1060 struct note_type
*ntype
= data
;
1062 if(--ntype
->refs
> 0)
1068 chanserv_flush_note_type(struct note_type
*ntype
)
1070 struct chanData
*cData
;
1071 for(cData
= channelList
; cData
; cData
= cData
->next
)
1072 dict_remove(cData
->notes
, ntype
->name
);
1076 chanserv_truncate_notes(struct note_type
*ntype
)
1078 struct chanData
*cData
;
1080 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1082 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1083 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1086 if(strlen(note
->note
) <= ntype
->max_length
)
1088 dict_remove2(cData
->notes
, ntype
->name
, 1);
1089 note
= realloc(note
, size
);
1090 note
->note
[ntype
->max_length
] = 0;
1091 dict_insert(cData
->notes
, ntype
->name
, note
);
1095 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1097 static struct note
*
1098 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1101 unsigned int len
= strlen(text
);
1103 if(len
> type
->max_length
) len
= type
->max_length
;
1104 note
= calloc(1, sizeof(*note
) + len
);
1106 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1107 memcpy(note
->note
, text
, len
);
1108 note
->note
[len
] = 0;
1109 dict_insert(channel
->notes
, type
->name
, note
);
1115 chanserv_free_note(void *data
)
1117 struct note
*note
= data
;
1119 chanserv_deref_note_type(note
->type
);
1120 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1124 static MODCMD_FUNC(cmd_createnote
) {
1125 struct note_type
*ntype
;
1126 unsigned int arg
= 1, existed
= 0, max_length
;
1128 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1131 ntype
= chanserv_create_note_type(argv
[arg
]);
1132 if(!irccasecmp(argv
[++arg
], "privileged"))
1135 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1136 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1138 else if(!irccasecmp(argv
[arg
], "channel"))
1140 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1143 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1146 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1147 ntype
->set_access
.min_ulevel
= ulvl
;
1149 else if(!irccasecmp(argv
[arg
], "setter"))
1151 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1155 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1159 if(!irccasecmp(argv
[++arg
], "privileged"))
1160 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1161 else if(!irccasecmp(argv
[arg
], "channel_users"))
1162 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1163 else if(!irccasecmp(argv
[arg
], "all"))
1164 ntype
->visible_type
= NOTE_VIS_ALL
;
1166 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1170 if((arg
+1) >= argc
) {
1171 reply("MSG_MISSING_PARAMS", argv
[0]);
1174 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1175 if(max_length
< 20 || max_length
> 450)
1177 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1180 if(existed
&& (max_length
< ntype
->max_length
))
1182 ntype
->max_length
= max_length
;
1183 chanserv_truncate_notes(ntype
);
1185 ntype
->max_length
= max_length
;
1188 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1190 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1195 dict_remove(note_types
, ntype
->name
);
1199 static MODCMD_FUNC(cmd_removenote
) {
1200 struct note_type
*ntype
;
1203 ntype
= dict_find(note_types
, argv
[1], NULL
);
1204 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1207 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1214 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1217 chanserv_flush_note_type(ntype
);
1219 dict_remove(note_types
, argv
[1]);
1220 reply("CSMSG_NOTE_DELETED", argv
[1]);
1225 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1229 if(orig
->modes_set
& change
->modes_clear
)
1231 if(orig
->modes_clear
& change
->modes_set
)
1233 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1234 && strcmp(orig
->new_key
, change
->new_key
))
1236 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1237 && (orig
->new_limit
!= change
->new_limit
))
1242 static char max_length_text
[MAXLEN
+1][16];
1244 static struct helpfile_expansion
1245 chanserv_expand_variable(const char *variable
)
1247 struct helpfile_expansion exp
;
1249 if(!irccasecmp(variable
, "notes"))
1252 exp
.type
= HF_TABLE
;
1253 exp
.value
.table
.length
= 1;
1254 exp
.value
.table
.width
= 3;
1255 exp
.value
.table
.flags
= 0;
1256 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1257 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1258 exp
.value
.table
.contents
[0][0] = "Note Type";
1259 exp
.value
.table
.contents
[0][1] = "Visibility";
1260 exp
.value
.table
.contents
[0][2] = "Max Length";
1261 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1263 struct note_type
*ntype
= iter_data(it
);
1266 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1267 row
= exp
.value
.table
.length
++;
1268 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1269 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1270 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1271 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1273 if(!max_length_text
[ntype
->max_length
][0])
1274 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1275 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1280 exp
.type
= HF_STRING
;
1281 exp
.value
.str
= NULL
;
1285 static struct chanData
*
1286 register_channel(struct chanNode
*cNode
, char *registrar
)
1288 struct chanData
*channel
;
1289 enum levelOption lvlOpt
;
1290 enum charOption chOpt
;
1292 channel
= calloc(1, sizeof(struct chanData
));
1294 channel
->notes
= dict_new();
1295 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1297 channel
->registrar
= strdup(registrar
);
1298 channel
->registered
= now
;
1299 channel
->visited
= now
;
1300 channel
->limitAdjusted
= now
;
1301 channel
->ownerTransfer
= now
;
1302 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1303 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1304 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1305 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1306 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1308 channel
->prev
= NULL
;
1309 channel
->next
= channelList
;
1312 channelList
->prev
= channel
;
1313 channelList
= channel
;
1314 registered_channels
++;
1316 channel
->channel
= cNode
;
1318 cNode
->channel_info
= channel
;
1323 static struct userData
*
1324 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access_level
, time_t seen
, const char *info
, time_t accessexpiry
)
1326 struct userData
*ud
;
1328 if(access_level
> UL_OWNER
)
1331 ud
= calloc(1, sizeof(*ud
));
1332 ud
->channel
= channel
;
1333 ud
->handle
= handle
;
1335 ud
->access
= access_level
;
1336 ud
->info
= info
? strdup(info
) : NULL
;
1337 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1342 ud
->next
= channel
->users
;
1344 channel
->users
->prev
= ud
;
1345 channel
->users
= ud
;
1347 channel
->userCount
++;
1351 ud
->u_next
= ud
->handle
->channels
;
1353 ud
->u_next
->u_prev
= ud
;
1354 ud
->handle
->channels
= ud
;
1356 ud
->flags
= USER_FLAGS_DEFAULT
;
1360 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1363 chanserv_expire_tempuser(void *data
)
1365 struct userData
*uData
= data
;
1369 handle
= strdup(uData
->handle
->handle
);
1370 if (uData
->accessexpiry
> 0) {
1371 if (uData
->present
) {
1372 struct userNode
*user
, *next_un
= NULL
;
1373 struct handle_info
*hi
;
1375 hi
= get_handle_info(handle
);
1376 for (user
= hi
->users
; user
; user
= next_un
) {
1377 struct mod_chanmode
*change
;
1378 struct modeNode
*mn
;
1379 unsigned int count
= 0;
1381 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1382 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1383 next_un
= user
->next_authed
;
1387 change
= mod_chanmode_alloc(2);
1388 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1389 change
->args
[count
++].u
.member
= mn
;
1392 change
->argc
= count
;
1393 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1395 mod_chanmode_free(change
);
1396 next_un
= user
->next_authed
;
1399 del_channel_user(uData
, 1);
1405 chanserv_expire_tempclvl(void *data
)
1407 struct userData
*uData
= data
;
1411 handle
= strdup(uData
->handle
->handle
);
1412 if (uData
->clvlexpiry
> 0) {
1413 int changemodes
= 0;
1414 unsigned int mode
= 0;
1416 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1418 mode
= MODE_REMOVE
| MODE_CHANOP
;
1419 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1421 mode
= MODE_REMOVE
| MODE_HALFOP
;
1425 if (uData
->present
) {
1426 struct userNode
*user
, *next_un
= NULL
;
1427 struct handle_info
*hi
;
1429 hi
= get_handle_info(handle
);
1430 for (user
= hi
->users
; user
; user
= next_un
) {
1431 struct mod_chanmode
*change
;
1432 struct modeNode
*mn
;
1433 unsigned int count
= 0;
1435 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1436 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1437 next_un
= user
->next_authed
;
1441 if (changemodes
== 0) {
1442 next_un
= user
->next_authed
;
1446 change
= mod_chanmode_alloc(2);
1447 change
->args
[count
].mode
= mode
;
1448 change
->args
[count
++].u
.member
= mn
;
1451 change
->argc
= count
;
1452 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1454 mod_chanmode_free(change
);
1455 next_un
= user
->next_authed
;
1459 uData
->access
= uData
->lastaccess
;
1460 uData
->lastaccess
= 0;
1461 uData
->clvlexpiry
= 0;
1467 del_channel_user(struct userData
*user
, int do_gc
)
1469 struct chanData
*channel
= user
->channel
;
1471 channel
->userCount
--;
1474 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1475 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1478 user
->prev
->next
= user
->next
;
1480 channel
->users
= user
->next
;
1482 user
->next
->prev
= user
->prev
;
1485 user
->u_prev
->u_next
= user
->u_next
;
1487 user
->handle
->channels
= user
->u_next
;
1489 user
->u_next
->u_prev
= user
->u_prev
;
1493 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1494 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1495 unregister_channel(channel
, "lost all users.");
1499 static struct adduserPending
*
1500 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1502 struct adduserPending
*ap
;
1503 ap
= calloc(1,sizeof(struct adduserPending
));
1504 ap
->channel
= channel
;
1507 ap
->created
= time(NULL
);
1509 /* ap->prev defaults to NULL already.. */
1510 ap
->next
= adduser_pendings
;
1511 if(adduser_pendings
)
1512 adduser_pendings
->prev
= ap
;
1513 adduser_pendings
= ap
;
1514 adduser_pendings_count
++;
1519 del_adduser_pending(struct adduserPending
*ap
)
1522 ap
->prev
->next
= ap
->next
;
1524 adduser_pendings
= ap
->next
;
1527 ap
->next
->prev
= ap
->prev
;
1531 static void expire_adduser_pending();
1533 /* find_adduser_pending(channel, user) will find an arbitrary record
1534 * from user, channel, or user and channel.
1535 * if user or channel are NULL, they will match any records.
1537 static struct adduserPending
*
1538 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1540 struct adduserPending
*ap
;
1542 expire_adduser_pending(); /* why not here.. */
1544 if(!channel
&& !user
) /* 2 nulls matches all */
1545 return(adduser_pendings
);
1546 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1548 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1555 /* Remove all pendings for a user or channel
1557 * called in nickserv.c DelUser() and proto-* unregister_channel()
1560 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1562 struct adduserPending
*ap
;
1564 /* So this is a bit wastefull, i hate dealing with linked lists.
1565 * if its a problem we'll rewrite it right */
1566 while((ap
= find_adduser_pending(channel
, user
))) {
1567 del_adduser_pending(ap
);
1571 /* Called from nickserv.c cmd_auth after someone auths */
1573 process_adduser_pending(struct userNode
*user
)
1575 struct adduserPending
*ap
;
1576 if(!user
->handle_info
)
1577 return; /* not associated with an account */
1578 while((ap
= find_adduser_pending(NULL
, user
)))
1580 struct userData
*actee
;
1581 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1583 /* Already on the userlist. do nothing*/
1587 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1588 scan_user_presence(actee
, NULL
);
1590 del_adduser_pending(ap
);
1595 expire_adduser_pending()
1597 struct adduserPending
*ap
, *ap_next
;
1598 ap
= adduser_pendings
;
1601 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1603 ap_next
= ap
->next
; /* save next */
1604 del_adduser_pending(ap
); /* free and relink */
1605 ap
= ap_next
; /* advance */
1612 static void expire_ban(void *data
);
1615 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1618 unsigned int ii
, l1
, l2
;
1623 bd
= malloc(sizeof(struct banData
));
1625 bd
->channel
= channel
;
1627 bd
->triggered
= triggered
;
1628 bd
->expires
= expires
;
1630 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1632 extern const char *hidden_host_suffix
;
1633 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1637 l2
= strlen(old_name
);
1640 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1642 new_mask
= alloca(MAXLEN
);
1643 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1646 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1648 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1649 bd
->reason
= strdup(reason
);
1652 timeq_add(expires
, expire_ban
, bd
);
1655 bd
->next
= channel
->bans
; /* lamers */
1657 channel
->bans
->prev
= bd
;
1659 channel
->banCount
++;
1666 del_channel_ban(struct banData
*ban
)
1668 ban
->channel
->banCount
--;
1672 ban
->prev
->next
= ban
->next
;
1674 ban
->channel
->bans
= ban
->next
;
1677 ban
->next
->prev
= ban
->prev
;
1680 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1689 expire_ban(void *data
) /* lamer.. */
1691 struct banData
*bd
= data
;
1692 if(!IsSuspended(bd
->channel
))
1694 struct banList bans
;
1695 struct mod_chanmode change
;
1697 bans
= bd
->channel
->channel
->banlist
;
1698 mod_chanmode_init(&change
);
1699 for(ii
=0; ii
<bans
.used
; ii
++)
1701 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1704 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1705 change
.args
[0].u
.hostmask
= bd
->mask
;
1706 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1712 del_channel_ban(bd
);
1715 static void chanserv_expire_suspension(void *data
);
1718 unregister_channel(struct chanData
*channel
, const char *reason
)
1720 struct mod_chanmode change
;
1721 char msgbuf
[MAXLEN
];
1723 /* After channel unregistration, the following must be cleaned
1725 - Channel information.
1727 - Channel bans. (lamers)
1728 - Channel suspension data.
1729 - adduser_pending data.
1730 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1736 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1740 mod_chanmode_init(&change
);
1741 change
.modes_clear
|= MODE_REGISTERED
;
1742 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1745 wipe_adduser_pending(channel
->channel
, NULL
);
1747 while(channel
->users
)
1748 del_channel_user(channel
->users
, 0);
1750 while(channel
->bans
)
1751 del_channel_ban(channel
->bans
);
1753 free(channel
->topic
);
1754 free(channel
->registrar
);
1755 free(channel
->greeting
);
1756 free(channel
->user_greeting
);
1757 free(channel
->topic_mask
);
1760 channel
->prev
->next
= channel
->next
;
1762 channelList
= channel
->next
;
1765 channel
->next
->prev
= channel
->prev
;
1767 if(channel
->suspended
)
1769 struct chanNode
*cNode
= channel
->channel
;
1770 struct suspended
*suspended
, *next_suspended
;
1772 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1774 next_suspended
= suspended
->previous
;
1775 free(suspended
->suspender
);
1776 free(suspended
->reason
);
1777 if(suspended
->expires
)
1778 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1783 cNode
->channel_info
= NULL
;
1785 channel
->channel
->channel_info
= NULL
;
1787 dict_delete(channel
->notes
);
1788 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1789 if(!IsSuspended(channel
))
1790 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1791 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1792 UnlockChannel(channel
->channel
);
1794 registered_channels
--;
1798 expire_channels(UNUSED_ARG(void *data
))
1800 struct chanData
*channel
, *next
;
1801 struct userData
*user
;
1802 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1804 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1805 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1807 for(channel
= channelList
; channel
; channel
= next
)
1809 next
= channel
->next
;
1811 /* See if the channel can be expired. */
1812 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1813 || IsProtected(channel
))
1816 /* Make sure there are no high-ranking users still in the channel. */
1817 for(user
=channel
->users
; user
; user
=user
->next
)
1818 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1823 /* Unregister the channel */
1824 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1825 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1826 unregister_channel(channel
, "registration expired.");
1829 if(chanserv_conf
.channel_expire_frequency
)
1830 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1834 expire_dnrs(UNUSED_ARG(void *data
))
1836 dict_iterator_t it
, next
;
1837 struct do_not_register
*dnr
;
1839 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
1841 dnr
= iter_data(it
);
1842 next
= iter_next(it
);
1843 if(dnr
->expires
&& dnr
->expires
<= now
)
1844 dict_remove(handle_dnrs
, dnr
->chan_name
+ 1);
1846 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
1848 dnr
= iter_data(it
);
1849 next
= iter_next(it
);
1850 if(dnr
->expires
&& dnr
->expires
<= now
)
1851 dict_remove(plain_dnrs
, dnr
->chan_name
+ 1);
1853 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
1855 dnr
= iter_data(it
);
1856 next
= iter_next(it
);
1857 if(dnr
->expires
&& dnr
->expires
<= now
)
1858 dict_remove(mask_dnrs
, dnr
->chan_name
+ 1);
1861 if(chanserv_conf
.dnr_expire_frequency
)
1862 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
1866 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1868 char protect
= channel
->chOpts
[chProtect
];
1869 struct userData
*cs_victim
, *cs_aggressor
;
1871 /* If victim access level is greater than set invitelevel, don't let
1872 * us kick them, but don't consider it punishment if someone else does
1876 if(victim
== aggressor
)
1878 /* Don't protect if the victim isn't authenticated (because they
1879 can't be a channel user), unless we are to protect non-users
1882 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1884 /* If they have enough access to invite themselvs through a ban,
1885 * and its us kicking them, don't. -Rubin */
1886 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1892 if(protect
!= 'a' && !cs_victim
)
1895 /* Protect if the aggressor isn't a user because at this point,
1896 the aggressor can only be less than or equal to the victim. */
1898 /* Not protected from chanserv except above */
1899 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1900 if(aggressor
== chanserv
)
1903 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1907 /* If the aggressor was a user, then the victim can't be helped. */
1914 if(cs_victim
->access
> cs_aggressor
->access
)
1919 if(cs_victim
->access
>= cs_aggressor
->access
)
1928 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1930 struct chanData
*cData
= channel
->channel_info
;
1931 struct userData
*cs_victim
;
1933 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1934 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1935 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1938 reply("CSMSG_OPBY_LOCKED");
1940 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1948 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1950 struct chanData
*cData
= channel
->channel_info
;
1951 struct userData
*cs_victim
;
1953 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1954 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1955 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1957 reply("CSMSG_HOPBY_LOCKED");
1966 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1968 if(IsService(victim
))
1970 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1974 if(protect_user(victim
, user
, channel
->channel_info
, false))
1976 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1984 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1986 if(IsService(victim
))
1988 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1992 if(protect_user(victim
, user
, channel
->channel_info
, false))
1994 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2001 static struct do_not_register
*
2002 chanserv_add_dnr(const char *chan_name
, const char *setter
, time_t expires
, const char *reason
)
2004 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
2005 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
2006 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
2007 strcpy(dnr
->reason
, reason
);
2009 dnr
->expires
= expires
;
2010 if(dnr
->chan_name
[0] == '*')
2011 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
2012 else if(strpbrk(dnr
->chan_name
, "*?"))
2013 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
2015 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
2019 static struct dnrList
2020 chanserv_find_dnrs(const char *chan_name
, const char *handle
, unsigned int max
)
2022 struct dnrList list
;
2023 dict_iterator_t it
, next
;
2024 struct do_not_register
*dnr
;
2026 dnrList_init(&list
);
2028 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
2030 if(dnr
->expires
&& dnr
->expires
<= now
)
2031 dict_remove(handle_dnrs
, handle
);
2032 else if (list
.used
< max
)
2033 dnrList_append(&list
, dnr
);
2036 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
2038 if(dnr
->expires
&& dnr
->expires
<= now
)
2039 dict_remove(plain_dnrs
, chan_name
);
2040 else if (list
.used
< max
)
2041 dnrList_append(&list
, dnr
);
2045 for(it
= dict_first(mask_dnrs
); it
&& list
.used
< max
; it
= next
)
2047 next
= iter_next(it
);
2048 if(!match_ircglob(chan_name
, iter_key(it
)))
2050 dnr
= iter_data(it
);
2051 if(dnr
->expires
&& dnr
->expires
<= now
)
2052 dict_remove(mask_dnrs
, iter_key(it
));
2054 dnrList_append(&list
, dnr
);
2060 static int dnr_print_func(struct do_not_register
*dnr
, void *extra
)
2062 struct userNode
*user
;
2063 char buf1
[INTERVALLEN
];
2064 char buf2
[INTERVALLEN
];
2068 strftime(buf1
, sizeof(buf1
), "%d %b %Y", localtime(&dnr
->set
));
2071 strftime(buf2
, sizeof(buf2
), "%d %b %Y", localtime(&dnr
->expires
));
2072 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET_EXPIRES", dnr
->chan_name
, buf1
, dnr
->setter
, buf2
, dnr
->reason
);
2076 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf1
, dnr
->setter
, dnr
->reason
);
2079 send_message(user
, chanserv
, "CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2084 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
2086 struct dnrList list
;
2089 list
= chanserv_find_dnrs(chan_name
, handle
, UINT_MAX
);
2090 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
2091 dnr_print_func(list
.list
[ii
], user
);
2093 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2098 struct do_not_register
*
2099 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2101 struct dnrList list
;
2102 struct do_not_register
*dnr
;
2104 list
= chanserv_find_dnrs(chan_name
, handle
? handle
->handle
: NULL
, 1);
2105 dnr
= list
.used
? list
.list
[0] : NULL
;
2110 static unsigned int send_dnrs(struct userNode
*user
, dict_t dict
)
2112 struct do_not_register
*dnr
;
2113 dict_iterator_t it
, next
;
2114 unsigned int matches
= 0;
2116 for(it
= dict_first(dict
); it
; it
= next
)
2118 dnr
= iter_data(it
);
2119 next
= iter_next(it
);
2120 if(dnr
->expires
&& dnr
->expires
<= now
)
2122 dict_remove(dict
, iter_key(it
));
2125 dnr_print_func(dnr
, user
);
2132 static CHANSERV_FUNC(cmd_noregister
)
2136 time_t expiry
, duration
;
2137 unsigned int matches
;
2141 reply("CSMSG_DNR_SEARCH_RESULTS");
2142 matches
= send_dnrs(user
, handle_dnrs
);
2143 matches
+= send_dnrs(user
, plain_dnrs
);
2144 matches
+= send_dnrs(user
, mask_dnrs
);
2146 reply("MSG_MATCH_COUNT", matches
);
2148 reply("MSG_NO_MATCHES");
2154 if(!IsChannelName(target
) && (*target
!= '*'))
2156 reply("CSMSG_NOT_DNR", target
);
2164 reply("MSG_INVALID_DURATION", argv
[2]);
2168 if(!strcmp(argv
[2], "0"))
2170 else if((duration
= ParseInterval(argv
[2])))
2171 expiry
= now
+ duration
;
2174 reply("MSG_INVALID_DURATION", argv
[2]);
2178 reason
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
2180 if((*target
== '*') && !get_handle_info(target
+ 1))
2182 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2185 chanserv_add_dnr(target
, user
->handle_info
->handle
, expiry
, reason
);
2186 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2190 reply("CSMSG_DNR_SEARCH_RESULTS");
2191 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2194 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2196 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2198 reply("MSG_NO_MATCHES");
2202 static CHANSERV_FUNC(cmd_allowregister
)
2204 const char *chan_name
= argv
[1];
2206 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2207 || dict_remove(plain_dnrs
, chan_name
)
2208 || dict_remove(mask_dnrs
, chan_name
))
2210 reply("CSMSG_DNR_REMOVED", chan_name
);
2213 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2218 struct userNode
*source
;
2222 time_t min_set
, max_set
;
2223 time_t min_expires
, max_expires
;
2228 dnr_search_matches(const struct do_not_register
*dnr
, const struct dnr_search
*search
)
2230 return !((dnr
->set
< search
->min_set
)
2231 || (dnr
->set
> search
->max_set
)
2232 || (dnr
->expires
< search
->min_expires
)
2233 || (search
->max_expires
2234 && ((dnr
->expires
== 0)
2235 || (dnr
->expires
> search
->max_expires
)))
2236 || (search
->chan_mask
2237 && !match_ircglob(dnr
->chan_name
, search
->chan_mask
))
2238 || (search
->setter_mask
2239 && !match_ircglob(dnr
->setter
, search
->setter_mask
))
2240 || (search
->reason_mask
2241 && !match_ircglob(dnr
->reason
, search
->reason_mask
)));
2244 static struct dnr_search
*
2245 dnr_search_create(struct userNode
*user
, struct svccmd
*cmd
, unsigned int argc
, char *argv
[])
2247 struct dnr_search
*discrim
;
2250 discrim
= calloc(1, sizeof(*discrim
));
2251 discrim
->source
= user
;
2252 discrim
->chan_mask
= NULL
;
2253 discrim
->setter_mask
= NULL
;
2254 discrim
->reason_mask
= NULL
;
2255 discrim
->max_set
= INT_MAX
;
2256 discrim
->limit
= 50;
2258 for(ii
=0; ii
<argc
; ++ii
)
2262 reply("MSG_MISSING_PARAMS", argv
[ii
]);
2265 else if(0 == irccasecmp(argv
[ii
], "channel"))
2267 discrim
->chan_mask
= argv
[++ii
];
2269 else if(0 == irccasecmp(argv
[ii
], "setter"))
2271 discrim
->setter_mask
= argv
[++ii
];
2273 else if(0 == irccasecmp(argv
[ii
], "reason"))
2275 discrim
->reason_mask
= argv
[++ii
];
2277 else if(0 == irccasecmp(argv
[ii
], "limit"))
2279 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 0);
2281 else if(0 == irccasecmp(argv
[ii
], "set"))
2283 const char *cmp
= argv
[++ii
];
2286 discrim
->min_set
= now
- ParseInterval(cmp
+ 2);
2288 discrim
->min_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2289 } else if(cmp
[0] == '=') {
2290 discrim
->min_set
= discrim
->max_set
= now
- ParseInterval(cmp
+ 1);
2291 } else if(cmp
[0] == '>') {
2293 discrim
->max_set
= now
- ParseInterval(cmp
+ 2);
2295 discrim
->max_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2297 discrim
->max_set
= now
- (ParseInterval(cmp
) - 1);
2300 else if(0 == irccasecmp(argv
[ii
], "expires"))
2302 const char *cmp
= argv
[++ii
];
2305 discrim
->max_expires
= now
+ ParseInterval(cmp
+ 2);
2307 discrim
->max_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2308 } else if(cmp
[0] == '=') {
2309 discrim
->min_expires
= discrim
->max_expires
= now
+ ParseInterval(cmp
+ 1);
2310 } else if(cmp
[0] == '>') {
2312 discrim
->min_expires
= now
+ ParseInterval(cmp
+ 2);
2314 discrim
->min_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2316 discrim
->min_expires
= now
+ (ParseInterval(cmp
) - 1);
2321 reply("MSG_INVALID_CRITERIA", argv
[ii
]);
2332 typedef int (*dnr_search_func
)(struct do_not_register
*match
, void *extra
);
2335 dnr_search(struct dnr_search
*discrim
, dnr_search_func dsf
, void *data
)
2337 struct do_not_register
*dnr
;
2338 dict_iterator_t next
;
2343 /* Initialize local variables. */
2346 if(discrim
->chan_mask
)
2348 int shift
= (discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*') ? 2 : 0;
2349 if('\0' == discrim
->chan_mask
[shift
+ strcspn(discrim
->chan_mask
+shift
, "*?")])
2353 if(target_fixed
&& discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*')
2355 /* Check against account-based DNRs. */
2356 dnr
= dict_find(handle_dnrs
, discrim
->chan_mask
+ 2, NULL
);
2357 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2360 else if(target_fixed
)
2362 /* Check against channel-based DNRs. */
2363 dnr
= dict_find(plain_dnrs
, discrim
->chan_mask
, NULL
);
2364 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2369 /* Exhaustively search account DNRs. */
2370 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
2372 next
= iter_next(it
);
2373 dnr
= iter_data(it
);
2374 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2378 /* Do the same for channel DNRs. */
2379 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
2381 next
= iter_next(it
);
2382 dnr
= iter_data(it
);
2383 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2387 /* Do the same for wildcarded channel DNRs. */
2388 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
2390 next
= iter_next(it
);
2391 dnr
= iter_data(it
);
2392 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2400 dnr_remove_func(struct do_not_register
*match
, void *extra
)
2402 struct userNode
*user
;
2405 chan_name
= alloca(strlen(match
->chan_name
) + 1);
2406 strcpy(chan_name
, match
->chan_name
);
2408 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2409 || dict_remove(plain_dnrs
, chan_name
)
2410 || dict_remove(mask_dnrs
, chan_name
))
2412 send_message(user
, chanserv
, "CSMSG_DNR_REMOVED", chan_name
);
2418 dnr_count_func(struct do_not_register
*match
, void *extra
)
2420 return 0; (void)match
; (void)extra
;
2423 static MODCMD_FUNC(cmd_dnrsearch
)
2425 struct dnr_search
*discrim
;
2426 dnr_search_func action
;
2427 struct svccmd
*subcmd
;
2428 unsigned int matches
;
2431 sprintf(buf
, "dnrsearch %s", argv
[1]);
2432 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
2435 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2438 if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
2440 if(!irccasecmp(argv
[1], "print"))
2441 action
= dnr_print_func
;
2442 else if(!irccasecmp(argv
[1], "remove"))
2443 action
= dnr_remove_func
;
2444 else if(!irccasecmp(argv
[1], "count"))
2445 action
= dnr_count_func
;
2448 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2452 discrim
= dnr_search_create(user
, cmd
, argc
-2, argv
+2);
2456 if(action
== dnr_print_func
)
2457 reply("CSMSG_DNR_SEARCH_RESULTS");
2458 matches
= dnr_search(discrim
, action
, user
);
2460 reply("MSG_MATCH_COUNT", matches
);
2462 reply("MSG_NO_MATCHES");
2468 chanserv_get_owned_count(struct handle_info
*hi
)
2470 struct userData
*cList
;
2473 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2474 if(cList
->access
== UL_OWNER
)
2479 static CHANSERV_FUNC(cmd_register
)
2481 struct handle_info
*handle
;
2482 struct chanData
*cData
;
2483 struct modeNode
*mn
;
2484 char reason
[MAXLEN
];
2486 unsigned int new_channel
, force
=0;
2487 struct do_not_register
*dnr
;
2490 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2491 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2497 if(channel
->channel_info
)
2499 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2503 if(channel
->bad_channel
)
2505 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2509 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2511 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2516 chan_name
= channel
->name
;
2522 reply("MSG_MISSING_PARAMS", cmd
->name
);
2523 svccmd_send_help_brief(user
, chanserv
, cmd
);
2526 if(!IsChannelName(argv
[1]))
2528 reply("MSG_NOT_CHANNEL_NAME");
2532 if(opserv_bad_channel(argv
[1]))
2534 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2539 chan_name
= argv
[1];
2542 if(argc
>= (new_channel
+2))
2544 if(!IsHelping(user
))
2546 reply("CSMSG_PROXY_FORBIDDEN");
2550 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2552 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2553 dnr
= chanserv_is_dnr(chan_name
, handle
);
2555 /* Check if they are over the limit.. */
2556 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2558 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2565 handle
= user
->handle_info
;
2566 dnr
= chanserv_is_dnr(chan_name
, handle
);
2567 /* Check if they are over the limit.. */
2568 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2570 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2573 /* Check if another service is in the channel */
2575 for(n
= 0; n
< channel
->members
.used
; n
++)
2577 mn
= channel
->members
.list
[n
];
2578 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2580 reply("CSMSG_ANOTHER_SERVICE");
2587 if(!IsHelping(user
))
2588 reply("CSMSG_DNR_CHANNEL", chan_name
);
2590 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2594 /* now handled above for message specilization *
2595 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2597 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2602 if (chanserv_conf
.valid_channel_regex_set
) {
2603 int err
= regexec(&chanserv_conf
.valid_channel_regex
, chan_name
, 0, 0, 0);
2606 buff
[regerror(err
, &chanserv_conf
.valid_channel_regex
, buff
, sizeof(buff
))] = 0;
2607 log_module(CS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
2609 if(err
== REG_NOMATCH
) {
2610 reply("CSMSG_ILLEGAL_CHANNEL", chan_name
);
2616 channel
= AddChannel(chan_name
, now
, NULL
, NULL
, NULL
);
2618 cData
= register_channel(channel
, user
->handle_info
->handle
);
2619 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2620 cData
->modes
= chanserv_conf
.default_modes
;
2622 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2623 if (IsOffChannel(cData
))
2625 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2629 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2630 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2631 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2633 mod_chanmode_announce(chanserv
, channel
, change
);
2634 mod_chanmode_free(change
);
2637 /* Initialize the channel's max user record. */
2638 cData
->max
= channel
->members
.used
;
2639 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2641 if(handle
!= user
->handle_info
)
2642 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2645 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2646 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2647 handle
->handle
, user
->handle_info
->handle
);
2652 make_confirmation_string(struct userData
*uData
)
2654 static char strbuf
[16];
2659 for(src
= uData
->handle
->handle
; *src
; )
2660 accum
= accum
* 31 + toupper(*src
++);
2662 for(src
= uData
->channel
->channel
->name
; *src
; )
2663 accum
= accum
* 31 + toupper(*src
++);
2664 sprintf(strbuf
, "%08x", accum
);
2668 static CHANSERV_FUNC(cmd_unregister
)
2671 char reason
[MAXLEN
];
2672 struct chanData
*cData
;
2673 struct userData
*uData
;
2675 cData
= channel
->channel_info
;
2678 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2682 uData
= GetChannelUser(cData
, user
->handle_info
);
2683 if(!uData
|| (uData
->access
< UL_OWNER
))
2685 reply("CSMSG_NO_ACCESS");
2689 if(IsProtected(cData
))
2691 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2695 if(!IsHelping(user
))
2697 const char *confirm_string
;
2698 if(IsSuspended(cData
))
2700 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2703 confirm_string
= make_confirmation_string(uData
);
2704 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2706 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2711 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2712 name
= strdup(channel
->name
);
2713 unregister_channel(cData
, reason
);
2714 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2715 reply("CSMSG_UNREG_SUCCESS", name
);
2721 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2723 extern struct userNode
*spamserv
;
2724 struct mod_chanmode
*change
;
2726 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2728 change
= mod_chanmode_alloc(2);
2730 change
->args
[0].mode
= MODE_CHANOP
;
2731 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2732 change
->args
[1].mode
= MODE_CHANOP
;
2733 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2737 change
= mod_chanmode_alloc(1);
2739 change
->args
[0].mode
= MODE_CHANOP
;
2740 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2743 mod_chanmode_announce(chanserv
, channel
, change
);
2744 mod_chanmode_free(change
);
2747 static CHANSERV_FUNC(cmd_move
)
2749 struct mod_chanmode change
;
2750 struct chanNode
*target
;
2751 struct modeNode
*mn
;
2752 struct userData
*uData
;
2753 struct do_not_register
*dnr
;
2754 int chanserv_join
= 0, spamserv_join
;
2758 if(IsProtected(channel
->channel_info
))
2760 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2764 if(!IsChannelName(argv
[1]))
2766 reply("MSG_NOT_CHANNEL_NAME");
2770 if(opserv_bad_channel(argv
[1]))
2772 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2776 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2778 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2780 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2782 if(!IsHelping(user
))
2783 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2785 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2791 mod_chanmode_init(&change
);
2792 if(!(target
= GetChannel(argv
[1])))
2794 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2795 if(!IsSuspended(channel
->channel_info
))
2798 else if(target
->channel_info
)
2800 reply("CSMSG_ALREADY_REGGED", target
->name
);
2803 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2804 && !IsHelping(user
))
2806 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2809 else if(!IsSuspended(channel
->channel_info
))
2814 /* Clear MODE_REGISTERED from old channel, add it to new. */
2816 change
.modes_clear
= MODE_REGISTERED
;
2817 mod_chanmode_announce(chanserv
, channel
, &change
);
2818 change
.modes_clear
= 0;
2819 change
.modes_set
= MODE_REGISTERED
;
2820 mod_chanmode_announce(chanserv
, target
, &change
);
2823 /* Move the channel_info to the target channel; it
2824 shouldn't be necessary to clear timeq callbacks
2825 for the old channel. */
2826 target
->channel_info
= channel
->channel_info
;
2827 target
->channel_info
->channel
= target
;
2828 channel
->channel_info
= NULL
;
2830 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2833 ss_cs_join_channel(target
, spamserv_join
);
2835 if(!IsSuspended(target
->channel_info
))
2837 char reason2
[MAXLEN
];
2838 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2839 DelChannelUser(chanserv
, channel
, reason2
, 0);
2842 UnlockChannel(channel
);
2843 LockChannel(target
);
2844 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2845 channel
->name
, target
->name
, user
->handle_info
->handle
);
2847 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2852 merge_users(struct chanData
*source
, struct chanData
*target
)
2854 struct userData
*suData
, *tuData
, *next
;
2860 /* Insert the source's users into the scratch area. */
2861 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2862 dict_insert(merge
, suData
->handle
->handle
, suData
);
2864 /* Iterate through the target's users, looking for
2865 users common to both channels. The lower access is
2866 removed from either the scratch area or target user
2868 for(tuData
= target
->users
; tuData
; tuData
= next
)
2870 struct userData
*choice
;
2872 next
= tuData
->next
;
2874 /* If a source user exists with the same handle as a target
2875 channel's user, resolve the conflict by removing one. */
2876 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2880 /* Pick the data we want to keep. */
2881 /* If the access is the same, use the later seen time. */
2882 if(suData
->access
== tuData
->access
)
2883 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2884 else /* Otherwise, keep the higher access level. */
2885 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2887 /* Remove the user that wasn't picked. */
2888 if(choice
== tuData
)
2890 dict_remove(merge
, suData
->handle
->handle
);
2891 del_channel_user(suData
, 0);
2894 del_channel_user(tuData
, 0);
2897 /* Move the remaining users to the target channel. */
2898 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2900 suData
= iter_data(it
);
2902 /* Insert the user into the target channel's linked list. */
2903 suData
->prev
= NULL
;
2904 suData
->next
= target
->users
;
2905 suData
->channel
= target
;
2908 target
->users
->prev
= suData
;
2909 target
->users
= suData
;
2911 /* Update the user counts for the target channel; the
2912 source counts are left alone. */
2913 target
->userCount
++;
2916 /* Possible to assert (source->users == NULL) here. */
2917 source
->users
= NULL
;
2922 merge_bans(struct chanData
*source
, struct chanData
*target
)
2924 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2926 /* Hold on to the original head of the target ban list
2927 to avoid comparing source bans with source bans. */
2928 tFront
= target
->bans
;
2930 /* Perform a totally expensive O(n*m) merge, ick. */
2931 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2933 /* Flag to track whether the ban's been moved
2934 to the destination yet. */
2937 /* Possible to assert (sbData->prev == NULL) here. */
2938 sNext
= sbData
->next
;
2940 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2942 tNext
= tbData
->next
;
2944 /* Perform two comparisons between each source
2945 and target ban, conflicts are resolved by
2946 keeping the broader ban and copying the later
2947 expiration and triggered time. */
2948 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2950 /* There is a broader ban in the target channel that
2951 overrides one in the source channel; remove the
2952 source ban and break. */
2953 if(sbData
->expires
> tbData
->expires
)
2954 tbData
->expires
= sbData
->expires
;
2955 if(sbData
->triggered
> tbData
->triggered
)
2956 tbData
->triggered
= sbData
->triggered
;
2957 del_channel_ban(sbData
);
2960 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2962 /* There is a broader ban in the source channel that
2963 overrides one in the target channel; remove the
2964 target ban, fall through and move the source over. */
2965 if(tbData
->expires
> sbData
->expires
)
2966 sbData
->expires
= tbData
->expires
;
2967 if(tbData
->triggered
> sbData
->triggered
)
2968 sbData
->triggered
= tbData
->triggered
;
2969 if(tbData
== tFront
)
2971 del_channel_ban(tbData
);
2974 /* Source bans can override multiple target bans, so
2975 we allow a source to run through this loop multiple
2976 times, but we can only move it once. */
2981 /* Remove the source ban from the source ban list. */
2983 sbData
->next
->prev
= sbData
->prev
;
2985 /* Modify the source ban's associated channel. */
2986 sbData
->channel
= target
;
2988 /* Insert the ban into the target channel's linked list. */
2989 sbData
->prev
= NULL
;
2990 sbData
->next
= target
->bans
;
2993 target
->bans
->prev
= sbData
;
2994 target
->bans
= sbData
;
2996 /* Update the user counts for the target channel. */
3001 /* Possible to assert (source->bans == NULL) here. */
3002 source
->bans
= NULL
;
3006 merge_data(struct chanData
*source
, struct chanData
*target
)
3008 /* Use more recent visited and owner-transfer time; use older
3009 * registered time. Bitwise or may_opchan. Use higher max.
3010 * Do not touch last_refresh, ban count or user counts.
3012 if(source
->visited
> target
->visited
)
3013 target
->visited
= source
->visited
;
3014 if(source
->registered
< target
->registered
)
3015 target
->registered
= source
->registered
;
3016 if(source
->ownerTransfer
> target
->ownerTransfer
)
3017 target
->ownerTransfer
= source
->ownerTransfer
;
3018 if(source
->may_opchan
)
3019 target
->may_opchan
= 1;
3020 if(source
->max
> target
->max
)
3021 target
->max
= source
->max
;
3025 merge_channel(struct chanData
*source
, struct chanData
*target
)
3027 merge_users(source
, target
);
3028 merge_bans(source
, target
);
3029 merge_data(source
, target
);
3032 static CHANSERV_FUNC(cmd_merge
)
3034 struct userData
*target_user
;
3035 struct chanNode
*target
;
3036 char reason
[MAXLEN
];
3041 /* Make sure the target channel exists and is registered to the user
3042 performing the command. */
3043 if(!(target
= GetChannel(argv
[1])))
3045 reply("MSG_INVALID_CHANNEL");
3050 if (!irccasecmp("nodelete", argv
[2]))
3054 if(!target
->channel_info
)
3056 reply("CSMSG_NOT_REGISTERED", target
->name
);
3060 if(IsProtected(channel
->channel_info
))
3062 reply("CSMSG_MERGE_NODELETE");
3066 if(IsSuspended(target
->channel_info
))
3068 reply("CSMSG_MERGE_SUSPENDED");
3072 if(channel
== target
)
3074 reply("CSMSG_MERGE_SELF");
3078 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
3079 if(!target_user
|| (target_user
->access
< UL_OWNER
))
3081 reply("CSMSG_MERGE_NOT_OWNER");
3085 /* Merge the channel structures and associated data. */
3086 merge_channel(channel
->channel_info
, target
->channel_info
);
3087 spamserv_cs_move_merge(user
, channel
, target
, 0);
3088 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
3090 unregister_channel(channel
->channel_info
, reason
);
3091 reply("CSMSG_MERGE_SUCCESS", target
->name
);
3095 static CHANSERV_FUNC(cmd_opchan
)
3097 struct mod_chanmode change
;
3098 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
3100 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
3103 if(!IsInChannel(channel
,chanserv
)) {
3104 reply("CSMSG_NOT_IN_CHANNEL", channel
->name
);
3107 channel
->channel_info
->may_opchan
= 0;
3108 mod_chanmode_init(&change
);
3110 change
.args
[0].mode
= MODE_CHANOP
;
3111 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
3112 if(!change
.args
[0].u
.member
)
3114 reply("CSMSG_OUT_OF_CHANNEL", channel
->name
);
3117 mod_chanmode_announce(chanserv
, channel
, &change
);
3118 reply("CSMSG_OPCHAN_DONE", channel
->name
);
3122 static CHANSERV_FUNC(cmd_adduser
)
3124 struct userData
*actee
;
3125 struct userData
*actor
, *real_actor
;
3126 struct handle_info
*handle
= NULL
;
3127 struct adduserPending
*tmp
;
3128 unsigned short access_level
, override
= 0;
3132 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
3134 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
3138 access_level
= user_level_from_name(argv
[2], UL_OWNER
);
3141 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3145 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3146 real_actor
= GetTrueChannelAccess(channel
->channel_info
, user
->handle_info
);
3148 if(actor
->access
<= access_level
)
3150 reply("CSMSG_NO_BUMP_ACCESS");
3154 /* Trying to add someone with equal/more access */
3155 if (!real_actor
|| real_actor
->access
<= access_level
)
3156 override
= CMD_LOG_OVERRIDE
;
3158 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3160 /* 'kevin must first authenticate with AuthServ'. is sent to user */
3161 struct userNode
*unode
;
3162 unode
= GetUserH(argv
[1]); /* find user struct by nick */
3165 if(find_adduser_pending(channel
, unode
)) {
3166 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
3169 if(IsInChannel(channel
, unode
)) {
3170 reply("CSMSG_ADDUSER_PENDING");
3171 tmp
= add_adduser_pending(channel
, unode
, access_level
);
3172 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
3174 /* this results in user must auth AND not in chan errors. too confusing..
3176 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3184 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3186 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
3190 time_t accessexpiry
= 0;
3191 unsigned int duration
= 0;
3193 if ((duration
= ParseInterval(argv
[3])))
3194 accessexpiry
= now
+ duration
;
3197 actee
= add_channel_user(channel
->channel_info
, handle
, access_level
, 0, NULL
, accessexpiry
);
3198 scan_user_presence(actee
, NULL
);
3201 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
3203 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access_level
), access_level
);
3204 return 1 | override
;
3207 static CHANSERV_FUNC(cmd_clvl
)
3209 struct handle_info
*handle
;
3210 struct userData
*victim
;
3211 struct userData
*actor
, *real_actor
;
3212 unsigned short new_access
, override
= 0;
3213 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3217 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3218 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3220 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3223 if(handle
== user
->handle_info
&& !privileged
)
3225 reply("CSMSG_NO_SELF_CLVL");
3229 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3231 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3235 if(actor
->access
<= victim
->access
&& !privileged
)
3237 reply("MSG_USER_OUTRANKED", handle
->handle
);
3241 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
3245 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3249 if(new_access
>= actor
->access
&& !privileged
)
3251 reply("CSMSG_NO_BUMP_ACCESS");
3255 time_t clvlexpiry
= 0;
3256 unsigned int duration
= 0;
3258 if ((duration
= ParseInterval(argv
[3])))
3259 clvlexpiry
= now
+ duration
;
3263 if (victim
->accessexpiry
> 0) {
3264 reply("CSMSG_NO_BUMP_EXPIRY");
3268 victim
->clvlexpiry
= clvlexpiry
;
3269 victim
->lastaccess
= victim
->access
;
3270 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
3273 /* Trying to clvl a equal/higher user */
3274 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& handle
!= user
->handle_info
))
3275 override
= CMD_LOG_OVERRIDE
;
3276 /* Trying to clvl someone to equal/higher access */
3277 if(!real_actor
|| new_access
>= real_actor
->access
)
3278 override
= CMD_LOG_OVERRIDE
;
3279 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3280 * If they lower their own access it's not a big problem.
3282 victim
->access
= new_access
;
3283 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
3284 return 1 | override
;
3287 static CHANSERV_FUNC(cmd_deluser
)
3289 struct handle_info
*handle
;
3290 struct userData
*victim
;
3291 struct userData
*actor
, *real_actor
;
3292 unsigned short access_level
, override
= 0;
3297 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3298 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3300 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
3303 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3305 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3311 access_level
= user_level_from_name(argv
[1], UL_OWNER
);
3312 char *useraccess
= user_level_name_from_level(victim
->access
);
3315 reply("CSMSG_INVALID_ACCESS", argv
[1]);
3318 if(strcasecmp(argv
[1], useraccess
))
3320 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
3326 access_level
= victim
->access
;
3329 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
3331 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
3335 /* If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override */
3336 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& real_actor
!= victim
))
3337 override
= CMD_LOG_OVERRIDE
;
3339 chan_name
= strdup(channel
->name
);
3340 del_channel_user(victim
, 1);
3341 reply("CSMSG_DELETED_USER", handle
->handle
, access_level
, chan_name
);
3343 return 1 | override
;
3347 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
3349 struct userData
*actor
, *real_actor
, *uData
, *next
;
3350 unsigned int override
= 0;
3352 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3353 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3355 if(min_access
> max_access
)
3357 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3361 if((actor
->access
<= max_access
) && !IsHelping(user
))
3363 reply("CSMSG_NO_ACCESS");
3367 if(!real_actor
|| real_actor
->access
<= max_access
)
3368 override
= CMD_LOG_OVERRIDE
;
3370 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3374 if((uData
->access
>= min_access
)
3375 && (uData
->access
<= max_access
)
3376 && match_ircglob(uData
->handle
->handle
, mask
))
3377 del_channel_user(uData
, 1);
3380 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3381 return 1 | override
;
3384 static CHANSERV_FUNC(cmd_mdelowner
)
3386 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3389 static CHANSERV_FUNC(cmd_mdelcoowner
)
3391 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3394 static CHANSERV_FUNC(cmd_mdelmanager
)
3396 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3399 static CHANSERV_FUNC(cmd_mdelop
)
3401 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3404 static CHANSERV_FUNC(cmd_mdelhalfop
)
3406 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3409 static CHANSERV_FUNC(cmd_mdelpeon
)
3411 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3414 static CHANSERV_FUNC(cmd_mdelpal
)
3416 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3419 static CHANSERV_FUNC(cmd_levels
)
3421 struct helpfile_table tbl
;
3424 tbl
.length
= 6 + 1; // 6 levels
3427 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3428 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3429 tbl
.contents
[0][0] = "Level";
3430 tbl
.contents
[0][1] = "From";
3431 tbl
.contents
[0][2] = "-";
3432 tbl
.contents
[0][3] = "To";
3434 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3435 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3436 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3437 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3438 tbl
.contents
[ii
][3] = msnprintf(1, "");
3440 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3441 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3442 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3443 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3444 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3446 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3447 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3448 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3449 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3450 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3452 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3453 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3454 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3455 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3456 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3458 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3459 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3460 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3461 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3462 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3464 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3465 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3466 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3467 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3468 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3470 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3474 reply("CSMSG_LEVELS_HEADER");
3475 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3476 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3477 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3478 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3479 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3480 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3487 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3489 struct banData
*bData
, *next
;
3490 char interval
[INTERVALLEN
];
3495 limit
= now
- duration
;
3496 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3500 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3503 del_channel_ban(bData
);
3507 intervalString(interval
, duration
, user
->handle_info
);
3508 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3513 cmd_trim_users(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
3515 struct userData
*actor
, *uData
, *next
;
3516 char interval
[INTERVALLEN
];
3520 actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3521 if(min_access
> max_access
)
3523 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3527 if(!actor
|| actor
->access
<= max_access
)
3529 reply("CSMSG_NO_ACCESS");
3534 limit
= now
- duration
;
3535 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3539 if((uData
->seen
> limit
)
3541 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3544 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3545 || (!max_access
&& (uData
->access
< actor
->access
)))
3547 del_channel_user(uData
, 1);
3555 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3557 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3561 static CHANSERV_FUNC(cmd_trim
)
3563 unsigned long duration
;
3564 unsigned short min_level
, max_level
;
3569 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3570 duration
= ParseInterval(argv
[2]);
3573 reply("CSMSG_CANNOT_TRIM");
3577 if(!irccasecmp(argv
[1], "lamers"))
3579 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3582 else if(!irccasecmp(argv
[1], "users"))
3584 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3587 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3589 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3592 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3594 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3599 reply("CSMSG_INVALID_TRIM", argv
[1]);
3604 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3605 to the user. cmd_all takes advantage of this. */
3606 static CHANSERV_FUNC(cmd_up
)
3608 struct mod_chanmode change
;
3609 struct userData
*uData
;
3612 mod_chanmode_init(&change
);
3614 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3615 if(!change
.args
[0].u
.member
)
3618 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3622 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3626 reply("CSMSG_GODMODE_UP", argv
[0]);
3629 else if(uData
->access
>= UL_OP
)
3631 change
.args
[0].mode
= MODE_CHANOP
;
3632 errmsg
= "CSMSG_ALREADY_OPPED";
3634 else if(uData
->access
>= UL_HALFOP
)
3636 change
.args
[0].mode
= MODE_HALFOP
;
3637 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3639 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3641 change
.args
[0].mode
= MODE_VOICE
;
3642 errmsg
= "CSMSG_ALREADY_VOICED";
3647 reply("CSMSG_NO_ACCESS");
3650 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3651 if(!change
.args
[0].mode
)
3654 reply(errmsg
, channel
->name
);
3657 modcmd_chanmode_announce(&change
);
3661 static CHANSERV_FUNC(cmd_down
)
3663 struct mod_chanmode change
;
3665 mod_chanmode_init(&change
);
3667 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3668 if(!change
.args
[0].u
.member
)
3671 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3675 if(!change
.args
[0].u
.member
->modes
)
3678 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3682 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3683 modcmd_chanmode_announce(&change
);
3687 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
)
3689 struct userData
*cList
;
3691 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3693 if(IsSuspended(cList
->channel
)
3694 || IsUserSuspended(cList
)
3695 || !GetUserMode(cList
->channel
->channel
, user
))
3698 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3704 static CHANSERV_FUNC(cmd_upall
)
3706 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3709 static CHANSERV_FUNC(cmd_downall
)
3711 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3714 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3715 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3718 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
)
3720 unsigned int ii
, valid
;
3721 struct userNode
*victim
;
3722 struct mod_chanmode
*change
;
3724 change
= mod_chanmode_alloc(argc
- 1);
3726 for(ii
=valid
=0; ++ii
< argc
; )
3728 if(!(victim
= GetUserH(argv
[ii
])))
3730 change
->args
[valid
].mode
= mode
;
3731 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3732 if(!change
->args
[valid
].u
.member
)
3734 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3739 change
->argc
= valid
;
3740 if(valid
< (argc
-1))
3741 reply("CSMSG_PROCESS_FAILED");
3744 modcmd_chanmode_announce(change
);
3745 reply(action
, channel
->name
);
3747 mod_chanmode_free(change
);
3751 static CHANSERV_FUNC(cmd_op
)
3753 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3756 static CHANSERV_FUNC(cmd_hop
)
3758 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3761 static CHANSERV_FUNC(cmd_deop
)
3763 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3766 static CHANSERV_FUNC(cmd_dehop
)
3768 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3771 static CHANSERV_FUNC(cmd_voice
)
3773 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3776 static CHANSERV_FUNC(cmd_devoice
)
3778 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3782 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3789 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3791 struct modeNode
*mn
= channel
->members
.list
[ii
];
3793 if(IsService(mn
->user
))
3796 b
= user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
3802 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3806 victims
[(*victimCount
)++] = mn
;
3811 int is_extban(char *b
) {
3819 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3821 struct userNode
*victim
;
3822 struct modeNode
**victims
;
3823 unsigned int offset
, n
, victimCount
, duration
= 0;
3825 char *reason
= "Bye.", *ban
, *name
;
3826 char interval
[INTERVALLEN
];
3828 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3829 REQUIRE_PARAMS(offset
);
3832 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3833 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3835 /* Truncate the reason to a length of TOPICLEN, as
3836 the ircd does; however, leave room for an ellipsis
3837 and the kicker's nick. */
3838 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3842 if((victim
= GetUserH(argv
[1])))
3844 victims
= alloca(sizeof(victims
[0]));
3845 victims
[0] = GetUserMode(channel
, victim
);
3846 /* XXX: The comparison with ACTION_KICK is just because all
3847 * other actions can work on users outside the channel, and we
3848 * want to allow those (e.g. unbans) in that case. If we add
3849 * some other ejection action for in-channel users, change
3851 victimCount
= victims
[0] ? 1 : 0;
3853 if(IsService(victim
))
3856 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3860 if((action
== ACTION_KICK
) && !victimCount
)
3863 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3867 if(protect_user(victim
, user
, channel
->channel_info
, false))
3869 // This translates to send_message(user, cmd->parent->bot, ...)
3870 // if user is x3 (ctcp action) cmd is null and segfault.
3872 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3876 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3877 name
= victim
->nick
;
3879 else if(!is_ircmask(argv
[1]) && (*argv
[1] == '*'))
3881 struct handle_info
*hi
;
3882 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
3883 const char *accountname
= argv
[1] + 1;
3885 if(!(hi
= get_handle_info(accountname
)))
3887 reply("MSG_HANDLE_UNKNOWN", accountname
);
3891 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", hi
->handle
);
3892 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3894 b
= bad_channel_ban(channel
, user
, banmask
, &victimCount
, victims
);
3897 reply("CSMSG_MASK_PROTECTED", banmask
);
3901 reply("CSMSG_BAD_BAN", banmask
);
3905 if((action
== ACTION_KICK
) && (victimCount
== 0))
3907 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, banmask
);
3911 name
= ban
= strdup(banmask
);
3915 if(!is_ircmask(argv
[1]))
3918 reply("MSG_NICK_UNKNOWN", argv
[1]);
3922 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3924 b
= bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
);
3925 if(cmd
&& (b
== 1)) {
3926 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3929 else if(cmd
&& (b
== -1)) {
3930 reply("CSMSG_BAD_BAN", argv
[1]);
3933 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3934 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3936 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3937 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3938 some creativity, but its not x3's job to be the ban censor anyway. */
3939 if(is_overmask(argv
[1]))
3942 reply("CSMSG_LAME_MASK", argv
[1]);
3945 //TODO: We have no support to do protection etc etc so for now we dont let you use x3 to set extended bans.
3946 if(is_extban(argv
[1]))
3949 reply("CSMSG_NO_EXTBANS", argv
[1]);
3953 if((action
== ACTION_KICK
) && (victimCount
== 0))
3956 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3960 name
= ban
= strdup(argv
[1]);
3963 /* Truncate the ban in place if necessary; we must ensure
3964 that 'ban' is a valid ban mask before sanitizing it. */
3966 sanitize_ircmask(ban
);
3968 if(action
& ACTION_ADD_LAMER
)
3970 struct banData
*bData
, *next
;
3972 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3975 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3980 if(action
& ACTION_ADD_TIMED_LAMER
)
3982 duration
= ParseInterval(argv
[2]);
3987 reply("CSMSG_DURATION_TOO_LOW");
3991 else if(duration
> (86400 * 365 * 2))
3994 reply("CSMSG_DURATION_TOO_HIGH");
4001 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
4003 if(match_ircglobs(bData
->mask
, ban
))
4005 int exact
= !irccasecmp(bData
->mask
, ban
);
4007 /* The ban is redundant; there is already a ban
4008 with the same effect in place. */
4012 free(bData
->reason
);
4013 bData
->reason
= strdup(reason
);
4014 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
4016 reply("CSMSG_REASON_CHANGE", ban
);
4020 if(exact
&& bData
->expires
)
4024 /* If the ban matches an existing one exactly,
4025 extend the expiration time if the provided
4026 duration is longer. */
4027 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
4029 bData
->expires
= now
+ duration
;
4040 /* Delete the expiration timeq entry and
4041 requeue if necessary. */
4042 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
4045 timeq_add(bData
->expires
, expire_ban
, bData
);
4049 /* automated kickban, dont reply */
4052 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
4054 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4060 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
4067 if(match_ircglobs(ban
, bData
->mask
))
4069 /* The ban we are adding makes previously existing
4070 bans redundant; silently remove them. */
4071 del_channel_ban(bData
);
4075 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
);
4077 name
= ban
= strdup(bData
->mask
);
4081 /* WHAT DOES THIS DO?? -Rubin */
4082 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
4084 extern const char *hidden_host_suffix
;
4085 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
4087 unsigned int l1
, l2
;
4090 l2
= strlen(old_name
);
4093 if(irccasecmp(ban
+ l1
- l2
, old_name
))
4095 new_mask
= malloc(MAXLEN
);
4096 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
4098 name
= ban
= new_mask
;
4103 if(action
& ACTION_BAN
)
4105 unsigned int exists
;
4106 struct mod_chanmode
*change
;
4108 if(channel
->banlist
.used
>= MAXBANS
)
4111 reply("CSMSG_BANLIST_FULL", channel
->name
);
4116 exists
= ChannelBanExists(channel
, ban
);
4117 change
= mod_chanmode_alloc(victimCount
+ 1);
4118 for(n
= 0; n
< victimCount
; ++n
)
4120 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
4121 change
->args
[n
].u
.member
= victims
[n
];
4125 change
->args
[n
].mode
= MODE_BAN
;
4126 change
->args
[n
++].u
.hostmask
= ban
;
4130 modcmd_chanmode_announce(change
);
4132 mod_chanmode_announce(chanserv
, channel
, change
);
4133 mod_chanmode_free(change
);
4135 if(exists
&& (action
== ACTION_BAN
))
4138 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
4144 if(action
& ACTION_ADD_LAMER
)
4146 char kick_reason
[MAXLEN
];
4147 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4149 for(n
= 0; n
< victimCount
; n
++) {
4150 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
4151 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4155 else if(action
& ACTION_KICK
)
4157 char kick_reason
[MAXLEN
];
4158 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4160 for(n
= 0; n
< victimCount
; n
++) {
4161 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4167 /* No response, since it was automated. */
4169 else if(action
& ACTION_ADD_LAMER
)
4172 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
4174 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4176 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
4177 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
4178 else if(action
& ACTION_BAN
)
4179 reply("CSMSG_BAN_DONE", name
, channel
->name
);
4180 else if(action
& ACTION_KICK
&& victimCount
)
4181 reply("CSMSG_KICK_DONE", name
, channel
->name
);
4187 static CHANSERV_FUNC(cmd_kickban
)
4189 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
4192 static CHANSERV_FUNC(cmd_kick
)
4194 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
4197 static CHANSERV_FUNC(cmd_ban
)
4199 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
4202 static CHANSERV_FUNC(cmd_addlamer
)
4204 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
4207 static CHANSERV_FUNC(cmd_addtimedlamer
)
4209 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
4212 static struct mod_chanmode
*
4213 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
4215 struct mod_chanmode
*change
;
4216 unsigned char *match
;
4217 unsigned int ii
, count
;
4219 match
= alloca(bans
->used
);
4222 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4224 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
4225 MATCH_USENICK
| MATCH_VISIBLE
, 0);
4232 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4234 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
4241 change
= mod_chanmode_alloc(count
);
4242 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4246 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
4247 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
4249 assert(count
== change
->argc
);
4253 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
4255 unsigned int jj
, ii
, count
;
4257 struct chanData
*channel
;
4259 struct mod_chanmode
*change
;
4261 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
4262 /* Walk through every channel */
4263 for(channel
= channelList
; channel
; channel
= channel
->next
) {
4264 switch(channel
->chOpts
[chBanTimeout
])
4266 default: case '0': continue; /* Dont remove bans in this chan */
4267 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
4268 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
4269 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
4270 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
4271 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
4274 /* First find out how many bans were going to unset */
4275 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4276 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see below
4277 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
&& !is_extban(channel
->channel
->banlist
.list
[jj
]->ban
))
4281 /* At least one ban, so setup a removal */
4282 change
= mod_chanmode_alloc(count
);
4284 /* Walk over every ban in this channel.. */
4285 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4286 bn
= channel
->channel
->banlist
.list
[jj
];
4287 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see above
4288 if (bn
->set
< bantimeout
&& !is_extban(bn
->ban
)) {
4289 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
4291 /* Add this ban to the mode change */
4292 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4293 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
4295 /* Pull this ban out of the list */
4296 banList_remove(&(channel
->channel
->banlist
), bn
);
4301 /* Send the modes to IRC */
4302 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
4304 /* free memory from strdup above */
4305 for(ii
= 0; ii
< count
; ++ii
)
4306 free((char*)change
->args
[ii
].u
.hostmask
);
4308 mod_chanmode_free(change
);
4311 /* Set this function to run again */
4312 if(chanserv_conf
.ban_timeout_frequency
)
4313 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
4318 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
4320 struct userNode
*actee
;
4326 /* may want to allow a comma delimited list of users... */
4327 if(!(actee
= GetUserH(argv
[1])))
4329 if(!is_ircmask(argv
[1]) && *argv
[1] == '*')
4331 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
4332 const char *accountname
= argv
[1] + 1;
4334 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", accountname
);
4335 mask
= strdup(banmask
);
4337 else if(!is_ircmask(argv
[1]))
4339 reply("MSG_NICK_UNKNOWN", argv
[1]);
4344 mask
= strdup(argv
[1]);
4348 /* We don't sanitize the mask here because ircu
4350 if(action
& ACTION_UNBAN
)
4352 struct mod_chanmode
*change
;
4353 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
4358 modcmd_chanmode_announce(change
);
4359 for(ii
= 0; ii
< change
->argc
; ++ii
)
4360 free((char*)change
->args
[ii
].u
.hostmask
);
4361 mod_chanmode_free(change
);
4366 if(action
& ACTION_DEL_LAMER
)
4368 struct banData
*ban
, *next
;
4370 ban
= channel
->channel_info
->bans
; /* lamers */
4374 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
4377 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
4382 del_channel_ban(ban
);
4389 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
4391 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
4397 static CHANSERV_FUNC(cmd_unban
)
4399 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
4402 static CHANSERV_FUNC(cmd_dellamer
)
4404 /* it doesn't necessarily have to remove the channel ban - may want
4405 to make that an option. */
4406 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
4409 static CHANSERV_FUNC(cmd_unbanme
)
4411 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4412 long flags
= ACTION_UNBAN
;
4414 /* remove permanent bans if the user has the proper access. */
4415 if(uData
->access
>= UL_MANAGER
)
4416 flags
|= ACTION_DEL_LAMER
;
4418 argv
[1] = user
->nick
;
4419 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
4422 static CHANSERV_FUNC(cmd_unbanall
)
4424 struct mod_chanmode
*change
;
4427 if(!channel
->banlist
.used
)
4429 reply("CSMSG_NO_BANS", channel
->name
);
4433 // TODO: dont remove some kinds of extended bans such as ~c
4434 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4435 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4437 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4438 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4440 modcmd_chanmode_announce(change
);
4441 for(ii
= 0; ii
< change
->argc
; ++ii
)
4442 free((char*)change
->args
[ii
].u
.hostmask
);
4443 mod_chanmode_free(change
);
4444 reply("CSMSG_BANS_REMOVED", channel
->name
);
4448 static CHANSERV_FUNC(cmd_open
)
4450 struct mod_chanmode
*change
;
4453 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4455 change
= mod_chanmode_alloc(0);
4456 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4457 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4458 && channel
->channel_info
->modes
.modes_set
)
4459 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4460 modcmd_chanmode_announce(change
);
4461 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4462 for(ii
= 0; ii
< change
->argc
; ++ii
)
4463 free((char*)change
->args
[ii
].u
.hostmask
);
4464 mod_chanmode_free(change
);
4468 static CHANSERV_FUNC(cmd_myaccess
)
4470 static struct string_buffer sbuf
;
4471 struct handle_info
*target_handle
;
4472 struct userData
*uData
;
4475 target_handle
= user
->handle_info
;
4476 else if(!IsStaff(user
))
4478 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4481 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4484 if(!oper_outranks(user
, target_handle
))
4487 if(!target_handle
->channels
)
4489 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4493 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4494 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4496 struct chanData
*cData
= uData
->channel
;
4498 if(uData
->access
> UL_OWNER
)
4500 if(IsProtected(cData
)
4501 && (target_handle
!= user
->handle_info
)
4502 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4505 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4506 if(uData
->flags
!= 0)
4507 string_buffer_append(&sbuf
, ',');
4508 if(IsUserSuspended(uData
))
4509 string_buffer_append(&sbuf
, 's');
4510 if(IsUserAutoOp(uData
))
4512 if(uData
->access
>= UL_OP
)
4513 string_buffer_append(&sbuf
, 'o');
4514 else if(uData
->access
>= UL_HALFOP
)
4515 string_buffer_append(&sbuf
, 'h');
4516 else if(uData
->access
>= UL_PEON
)
4517 string_buffer_append(&sbuf
, 'v');
4519 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4520 string_buffer_append(&sbuf
, 'i');
4521 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4522 string_buffer_append(&sbuf
, 'j');
4524 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4526 string_buffer_append_string(&sbuf
, ")]");
4527 string_buffer_append(&sbuf
, '\0');
4528 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4534 static CHANSERV_FUNC(cmd_access
)
4536 struct userNode
*target
;
4537 struct handle_info
*target_handle
;
4538 struct userData
*uData
;
4540 char prefix
[MAXLEN
];
4545 target_handle
= target
->handle_info
;
4547 else if((target
= GetUserH(argv
[1])))
4549 target_handle
= target
->handle_info
;
4551 else if(argv
[1][0] == '*')
4553 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4555 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4561 reply("MSG_NICK_UNKNOWN", argv
[1]);
4565 assert(target
|| target_handle
);
4567 if(target
== chanserv
)
4569 reply("CSMSG_IS_CHANSERV");
4577 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4582 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4585 reply("MSG_AUTHENTICATE");
4591 const char *epithet
= NULL
, *type
= NULL
;
4594 epithet
= chanserv_conf
.irc_operator_epithet
;
4595 type
= user_find_message(user
, "CSMSG_OPERATOR_TITLE");
4597 else if(IsNetworkHelper(target
))
4599 epithet
= chanserv_conf
.network_helper_epithet
;
4600 type
= user_find_message(user
, "CSMSG_UC_H_TITLE");
4602 else if(IsSupportHelper(target
))
4604 epithet
= chanserv_conf
.support_helper_epithet
;
4605 type
= user_find_message(user
, "CSMSG_LC_H_TITLE");
4609 if(target_handle
->epithet
)
4610 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4612 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4614 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4618 sprintf(prefix
, "%s", target_handle
->handle
);
4621 if(!channel
->channel_info
)
4623 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4627 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4628 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4629 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4631 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4632 /* To prevent possible information leaks, only show infolines
4633 * if the requestor is in the channel or it's their own
4635 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4637 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4639 /* Likewise, only say it's suspended if the user has active
4640 * access in that channel or it's their own entry. */
4641 if(IsUserSuspended(uData
)
4642 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4643 || (user
->handle_info
== uData
->handle
)))
4645 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4650 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4656 /* This is never used...
4658 zoot_list(struct listData *list)
4660 struct userData *uData;
4661 unsigned int start, curr, highest, lowest;
4662 struct helpfile_table tmp_table;
4663 const char **temp, *msg;
4665 if(list->table.length == 1)
4668 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4670 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4671 msg = user_find_message(list->user, "MSG_NONE");
4672 send_message_type(4, list->user, list->bot, " %s", msg);
4674 tmp_table.width = list->table.width;
4675 tmp_table.flags = list->table.flags;
4676 list->table.contents[0][0] = " ";
4677 highest = list->highest;
4678 if(list->lowest != 0)
4679 lowest = list->lowest;
4680 else if(highest < 100)
4683 lowest = highest - 100;
4684 for(start = curr = 1; curr < list->table.length; )
4686 uData = list->users[curr-1];
4687 list->table.contents[curr++][0] = " ";
4688 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4691 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest), list->search);
4693 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest));
4694 temp = list->table.contents[--start];
4695 list->table.contents[start] = list->table.contents[0];
4696 tmp_table.contents = list->table.contents + start;
4697 tmp_table.length = curr - start;
4698 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4699 list->table.contents[start] = temp;
4701 highest = lowest - 1;
4702 lowest = (highest < 100) ? 0 : (highest - 99);
4709 normal_list(struct listData
*list
)
4713 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
), list
->search
);
4715 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
));
4716 if(list
->table
.length
== 1)
4718 msg
= user_find_message(list
->user
, "MSG_NONE");
4719 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4722 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4725 /* if these need changed, uncomment and customize
4727 clean_list(struct listData *list)
4731 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4733 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4734 if(list->table.length == 1)
4736 msg = user_find_message(list->user, "MSG_NONE");
4737 send_message_type(4, list->user, list->bot, " %s", msg);
4740 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4744 advanced_list(struct listData *list)
4748 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4750 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4751 if(list->table.length == 1)
4753 msg = user_find_message(list->user, "MSG_NONE");
4754 send_message_type(4, list->user, list->bot, " %s", msg);
4757 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4761 classic_list(struct listData *list)
4765 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4767 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4768 if(list->table.length == 1)
4770 msg = user_find_message(list->user, "MSG_NONE");
4771 send_message_type(4, list->user, list->bot, " %s", msg);
4774 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4779 userData_access_comp(const void *arg_a
, const void *arg_b
)
4781 const struct userData
*a
= *(struct userData
**)arg_a
;
4782 const struct userData
*b
= *(struct userData
**)arg_b
;
4784 if(a
->access
!= b
->access
)
4785 res
= b
->access
- a
->access
;
4787 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4792 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4794 void (*send_list
)(struct listData
*);
4795 struct userData
*uData
;
4796 struct listData lData
;
4797 unsigned int matches
;
4803 lData
.bot
= cmd
->parent
->bot
;
4804 lData
.channel
= channel
;
4805 lData
.lowest
= lowest
;
4806 lData
.highest
= highest
;
4807 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4808 send_list
= normal_list
;
4809 /* What does the following line do exactly?? */
4810 /*(void)zoot_list; ** since it doesn't show user levels */
4813 if(user->handle_info)
4815 switch(user->handle_info->userlist_style)
4817 case HI_STYLE_CLEAN:
4818 send_list = clean_list;
4820 case HI_STYLE_ADVANCED:
4821 send_list = advanced_list;
4823 case HI_STYLE_CLASSIC:
4824 send_list = classic_list;
4826 case HI_STYLE_NORMAL:
4828 send_list = normal_list;
4833 send_list
= normal_list
;
4835 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4837 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4839 if((uData
->access
< lowest
)
4840 || (uData
->access
> highest
)
4841 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4843 lData
.users
[matches
++] = uData
;
4845 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4847 lData
.table
.length
= matches
+1;
4848 lData
.table
.flags
= TABLE_NO_FREE
;
4849 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4851 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4852 lData
.table
.width
= 6; /* with level = 6 */
4854 lData
.table
.width
= 5; /* without = 5 */
4855 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4856 lData
.table
.contents
[0] = ary
;
4857 if(user
->handle_info
) {
4858 switch(user
->handle_info
->userlist_style
) {
4859 case HI_STYLE_CLASSIC
:
4862 case HI_STYLE_ADVANCED
:
4863 ary
[i
++] = "Access";
4866 case HI_STYLE_CLEAN
:
4867 ary
[i
++] = "Access";
4869 case HI_STYLE_NORMAL
:
4871 ary
[i
++] = "Access";
4876 ary
[i
++] = "Access";
4878 ary
[i
++] = "Account";
4879 ary
[i
] = "Last Seen";
4881 ary
[i
++] = "Status";
4882 ary
[i
++] = "Expiry";
4883 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4885 char seen
[INTERVALLEN
];
4888 uData
= lData
.users
[matches
-1];
4890 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4891 lData
.table
.contents
[matches
] = ary
;
4892 if(user
->handle_info
) {
4893 switch(user
->handle_info
->userlist_style
) {
4894 case HI_STYLE_CLASSIC
:
4895 ary
[i
++] = strtab(uData
->access
);
4897 case HI_STYLE_ADVANCED
:
4898 ary
[i
++] = user_level_name_from_level(uData
->access
);
4899 ary
[i
++] = strtab(uData
->access
);
4901 case HI_STYLE_CLEAN
:
4902 ary
[i
++] = user_level_name_from_level(uData
->access
);
4904 case HI_STYLE_NORMAL
:
4906 ary
[i
++] = user_level_name_from_level(uData
->access
);
4911 ary
[i
++] = user_level_name_from_level(uData
->access
);
4913 ary
[i
++] = uData
->handle
->handle
;
4916 else if(!uData
->seen
)
4919 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4920 ary
[i
] = strdup(ary
[i
]);
4922 if(IsUserSuspended(uData
))
4923 ary
[i
++] = "Suspended";
4924 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4925 ary
[i
++] = "Vacation";
4927 ary
[i
++] = "Normal";
4929 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4930 char delay
[INTERVALLEN
];
4933 if (uData
->accessexpiry
> 0) {
4934 diff
= uData
->accessexpiry
- now
;
4935 intervalString(delay
, diff
, user
->handle_info
);
4937 diff
= uData
->clvlexpiry
- now
;
4938 intervalString(delay
, diff
, user
->handle_info
);
4946 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4948 /* Free strdup above */
4949 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4950 free(lData
.table
.contents
[matches
]);
4952 free(lData
.table
.contents
[0]);
4953 free(lData
.table
.contents
);
4957 /* Remove this now that debugging is over? or improve it for
4958 * users? Would it be better tied into USERS somehow? -Rubin */
4959 static CHANSERV_FUNC(cmd_pending
)
4961 struct adduserPending
*ap
;
4962 reply("CSMSG_ADDUSER_PENDING_HEADER");
4963 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4965 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4966 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4967 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4971 static CHANSERV_FUNC(cmd_users
)
4973 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4976 static CHANSERV_FUNC(cmd_wlist
)
4978 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4981 static CHANSERV_FUNC(cmd_clist
)
4983 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4986 static CHANSERV_FUNC(cmd_mlist
)
4988 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4991 static CHANSERV_FUNC(cmd_olist
)
4993 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4996 static CHANSERV_FUNC(cmd_hlist
)
4998 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
5001 static CHANSERV_FUNC(cmd_plist
)
5003 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
5006 static CHANSERV_FUNC(cmd_lamers
)
5008 struct userNode
*search_u
= NULL
;
5009 struct helpfile_table tbl
;
5010 unsigned int matches
= 0, timed
= 0, search_wilds
= 0, ii
;
5011 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
5012 const char *msg_never
, *triggered
, *expires
;
5013 struct banData
*ban
, **bans
; /* lamers */
5017 else if(strchr(search
= argv
[1], '!'))
5020 search_wilds
= search
[strcspn(search
, "?*")];
5022 else if(!(search_u
= GetUserH(search
)))
5023 reply("MSG_NICK_UNKNOWN", search
);
5025 reply("CSMSG_LAMERS_HEADER", channel
->name
);
5026 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
5029 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
5033 if(!user_matches_glob(search_u
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
5038 if(search_wilds
? !match_ircglobs(search
, ban
->mask
) : !match_ircglob(search
, ban
->mask
))
5041 bans
[matches
++] = ban
;
5046 tbl
.length
= matches
+ 1;
5047 tbl
.width
= 4 + timed
;
5049 tbl
.flags
= TABLE_NO_FREE
;
5050 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
5051 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5052 tbl
.contents
[0][0] = "Mask";
5053 tbl
.contents
[0][1] = "Set By";
5054 tbl
.contents
[0][2] = "Triggered";
5057 tbl
.contents
[0][3] = "Expires";
5058 tbl
.contents
[0][4] = "Reason";
5061 tbl
.contents
[0][3] = "Reason";
5064 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5065 /* reply("MSG_NONE"); */
5066 free(tbl
.contents
[0]);
5071 msg_never
= user_find_message(user
, "MSG_NEVER");
5072 for(ii
= 0; ii
< matches
; )
5078 else if(ban
->expires
)
5079 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
5081 expires
= msg_never
;
5084 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
5086 triggered
= msg_never
;
5088 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5089 tbl
.contents
[ii
][0] = ban
->mask
;
5090 tbl
.contents
[ii
][1] = ban
->owner
;
5091 tbl
.contents
[ii
][2] = strdup(triggered
);
5094 tbl
.contents
[ii
][3] = strdup(expires
);
5095 tbl
.contents
[ii
][4] = ban
->reason
;
5098 tbl
.contents
[ii
][3] = ban
->reason
;
5100 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5101 /* reply("MSG_MATCH_COUNT", matches); */
5102 for(ii
= 1; ii
< tbl
.length
; ++ii
)
5104 free((char*)tbl
.contents
[ii
][2]);
5106 free((char*)tbl
.contents
[ii
][3]);
5107 free(tbl
.contents
[ii
]);
5109 free(tbl
.contents
[0]);
5116 * return + if the user does NOT have the right to set the topic, and
5117 * the topic is changed.
5120 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
5122 struct chanData
*cData
= channel
->channel_info
;
5123 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5125 else if(cData
->topic
)
5126 return irccasecmp(new_topic
, cData
->topic
);
5133 * Makes a givin topic fit into a givin topic mask and returns
5136 * topic_mask - the mask to conform to
5137 * topic - the topic to make conform
5138 * new_topic - the pre-allocated char* to put the new topic into
5140 * modifies: new_topic
5143 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
5145 //char *topic_mask = cData->topic_mask;
5147 int pos
=0, starpos
=-1, dpos
=0, len
;
5149 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
5156 strcpy(new_topic
, "");
5159 len
= strlen(topic
);
5160 if((dpos
+ len
) > TOPICLEN
)
5161 len
= TOPICLEN
+ 1 - dpos
;
5162 memcpy(new_topic
+dpos
, topic
, len
);
5166 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
5167 default: new_topic
[dpos
++] = tchar
; break;
5170 if((dpos
> TOPICLEN
) || tchar
)
5172 strcpy(new_topic
, "");
5175 new_topic
[dpos
] = 0;
5179 static CHANSERV_FUNC(cmd_topic
)
5181 struct chanData
*cData
;
5185 #ifdef WITH_PROTOCOL_P10
5189 cData
= channel
->channel_info
;
5194 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5195 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
5196 reply("CSMSG_TOPIC_SET", cData
->topic
);
5200 reply("CSMSG_NO_TOPIC", channel
->name
);
5204 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5205 /* If they say "!topic *", use an empty topic. */
5206 if((topic
[0] == '*') && (topic
[1] == 0))
5209 if(bad_topic(channel
, user
, topic
))
5211 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5216 /* If there is a topicmask set, and the new topic doesnt match, make it */
5217 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
5219 char *topic_mask
= cData
->topic_mask
;
5220 char new_topic
[TOPICLEN
+1];
5222 /* make a new topic fitting mask */
5223 conform_topic(topic_mask
, topic
, new_topic
);
5226 /* Topic couldnt fit into mask, was too long */
5227 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
5228 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
5231 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
5233 else /* No mask set, just set the topic */
5234 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
5237 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
5239 /* Grab the topic and save it as the default topic. */
5241 cData
->topic
= strdup(channel
->topic
);
5247 static CHANSERV_FUNC(cmd_mode
)
5249 struct userData
*uData
;
5250 struct mod_chanmode
*change
;
5255 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5256 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5260 change
= &channel
->channel_info
->modes
;
5261 if(change
->modes_set
|| change
->modes_clear
) {
5262 modcmd_chanmode_announce(change
);
5263 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
5265 reply("CSMSG_NO_MODES", channel
->name
);
5269 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5271 base_oplevel
= MAXOPLEVEL
;
5272 else if (uData
->access
>= UL_OWNER
)
5275 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
5276 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
5280 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
5284 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
5285 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
5288 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5289 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
5293 modcmd_chanmode_announce(change
);
5294 mod_chanmode_free(change
);
5295 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
5299 static CHANSERV_FUNC(cmd_invite
)
5301 struct userData
*uData
;
5302 struct userNode
*invite
;
5304 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5308 if(!(invite
= GetUserH(argv
[1])))
5310 reply("MSG_NICK_UNKNOWN", argv
[1]);
5317 if(GetUserMode(channel
, invite
))
5319 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
5327 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5328 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
5331 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
5334 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
5336 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
5337 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
, 0)) {
5338 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
5344 irc_invite(chanserv
, invite
, channel
);
5346 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
5351 static CHANSERV_FUNC(cmd_inviteme
)
5353 if(GetUserMode(channel
, user
))
5355 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
5358 if(channel
->channel_info
5359 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
5361 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
5364 irc_invite(cmd
->parent
->bot
, user
, channel
);
5369 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
5372 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
5374 /* We display things based on two dimensions:
5375 * - Issue time: present or absent
5376 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5377 * (in order of precedence, so something both expired and revoked
5378 * only counts as revoked)
5380 combo
= (suspended
->issued
? 4 : 0)
5381 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
5383 case 0: /* no issue time, indefinite expiration */
5384 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
5386 case 1: /* no issue time, expires in future */
5387 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
5388 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
5390 case 2: /* no issue time, expired */
5391 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
5392 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
5394 case 3: /* no issue time, revoked */
5395 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
5396 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
5398 case 4: /* issue time set, indefinite expiration */
5399 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5400 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
5402 case 5: /* issue time set, expires in future */
5403 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5404 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
5405 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5407 case 6: /* issue time set, expired */
5408 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5409 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
5410 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5412 case 7: /* issue time set, revoked */
5413 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5414 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
5415 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5418 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
5424 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
5427 const char *fmt
= "%a %b %d %H:%M %Y";
5428 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
5430 if(giveownership
->staff_issuer
)
5432 if(giveownership
->reason
)
5433 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
5434 giveownership
->target
, giveownership
->target_access
,
5435 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
5437 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
5438 giveownership
->target
, giveownership
->target_access
,
5439 giveownership
->staff_issuer
, buf
);
5443 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5448 static CHANSERV_FUNC(cmd_info
)
5450 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5451 struct userData
*uData
, *owner
;
5452 struct chanData
*cData
;
5453 struct do_not_register
*dnr
;
5458 cData
= channel
->channel_info
;
5459 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5460 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5463 uData
= GetChannelUser(cData
, user
->handle_info
);
5464 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5466 mod_chanmode_format(&cData
->modes
, modes
);
5467 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5468 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5471 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5475 note
= iter_data(it
);
5476 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5479 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5480 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5483 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5484 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5485 if(owner
->access
== UL_OWNER
)
5486 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5487 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5488 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5489 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5491 privileged
= IsStaff(user
);
5492 /* if(privileged) */
5493 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5494 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5495 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5497 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5498 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5500 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5502 struct suspended
*suspended
;
5503 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5504 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5505 show_suspension_info(cmd
, user
, suspended
);
5507 else if(IsSuspended(cData
))
5509 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5510 show_suspension_info(cmd
, user
, cData
->suspended
);
5512 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5514 struct giveownership
*giveownership
;
5515 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5516 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5517 show_giveownership_info(cmd
, user
, giveownership
);
5519 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5520 reply("CSMSG_CHANNEL_END");
5522 reply("CSMSG_CHANNEL_END_CLEAN");
5526 static CHANSERV_FUNC(cmd_netinfo
)
5528 extern time_t boot_time
;
5529 extern unsigned long burst_length
;
5530 char interval
[INTERVALLEN
];
5532 reply("CSMSG_NETWORK_INFO");
5533 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5534 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5535 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5536 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5537 reply("CSMSG_NETWORK_LAMERS", banCount
);
5538 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5539 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5540 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5545 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5547 struct helpfile_table table
;
5549 struct userNode
*user
;
5554 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5555 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5556 for(nn
=0; nn
<list
->used
; nn
++)
5558 user
= list
->list
[nn
];
5559 if(user
->modes
& skip_flags
)
5561 if(IsBot(user
) || IsHideOper(user
))
5563 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5566 nick
= alloca(strlen(user
->nick
)+3);
5567 sprintf(nick
, "(%s)", user
->nick
);
5571 table
.contents
[table
.length
][0] = nick
;
5574 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5577 static CHANSERV_FUNC(cmd_ircops
)
5579 reply("CSMSG_STAFF_OPERS");
5580 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5584 static CHANSERV_FUNC(cmd_helpers
)
5586 reply("CSMSG_STAFF_HELPERS");
5587 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5591 static CHANSERV_FUNC(cmd_staff
)
5593 reply("CSMSG_NETWORK_STAFF");
5594 cmd_ircops(CSFUNC_ARGS
);
5595 cmd_helpers(CSFUNC_ARGS
);
5599 static CHANSERV_FUNC(cmd_peek
)
5601 struct modeNode
*mn
;
5602 char modes
[MODELEN
];
5604 struct helpfile_table table
;
5606 irc_make_chanmode(channel
, modes
);
5608 reply("CSMSG_PEEK_INFO", channel
->name
);
5609 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5611 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5612 reply("CSMSG_PEEK_MODES", modes
);
5613 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5617 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5618 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5619 for(n
= 0; n
< channel
->members
.used
; n
++)
5621 mn
= channel
->members
.list
[n
];
5622 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5624 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5625 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5630 reply("CSMSG_PEEK_OPS");
5631 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5634 reply("CSMSG_PEEK_NO_OPS");
5635 reply("CSMSG_PEEK_END");
5639 static MODCMD_FUNC(cmd_wipeinfo
)
5641 struct handle_info
*victim
;
5642 struct userData
*ud
, *actor
, *real_actor
;
5643 unsigned int override
= 0;
5646 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5647 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5648 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5650 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5652 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5655 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5657 reply("MSG_USER_OUTRANKED", victim
->handle
);
5660 if((ud
!= real_actor
) && (!real_actor
|| (ud
->access
>= real_actor
->access
)))
5661 override
= CMD_LOG_OVERRIDE
;
5665 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5666 return 1 | override
;
5670 resync_channel(struct chanNode
*channel
)
5672 struct mod_chanmode
*changes
;
5673 struct chanData
*cData
= channel
->channel_info
;
5674 unsigned int ii
, used
;
5676 /* 6 = worst case -ovh+ovh on everyone */
5677 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5678 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5680 struct modeNode
*mn
= channel
->members
.list
[ii
];
5681 struct userData
*uData
;
5683 if(IsService(mn
->user
))
5687 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5689 /* If the channel is in no-mode mode, de-mode EVERYONE */
5690 if(cData
->chOpts
[chAutomode
] == 'n')
5694 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5695 changes
->args
[used
++].u
.member
= mn
;
5698 else /* Give various userlevels their modes.. */
5700 /* If the user has autoop/autovoice disabled then ignore them */
5701 if(uData
&& !IsUserAutoOp(uData
))
5703 if(uData
&& uData
->access
>= UL_OP
)
5705 if(!(mn
->modes
& MODE_CHANOP
))
5707 changes
->args
[used
].mode
= MODE_CHANOP
;
5708 changes
->args
[used
++].u
.member
= mn
;
5711 else if(uData
&& uData
->access
>= UL_HALFOP
)
5713 if(mn
->modes
& MODE_CHANOP
)
5715 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5716 changes
->args
[used
++].u
.member
= mn
;
5718 if(!(mn
->modes
& MODE_HALFOP
))
5720 changes
->args
[used
].mode
= MODE_HALFOP
;
5721 changes
->args
[used
++].u
.member
= mn
;
5724 else if(uData
&& uData
->access
>= UL_PEON
)
5726 if(mn
->modes
& MODE_CHANOP
)
5728 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5729 changes
->args
[used
++].u
.member
= mn
;
5731 if(mn
->modes
& MODE_HALFOP
)
5733 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5734 changes
->args
[used
++].u
.member
= mn
;
5736 /* Don't voice peons if were in mode m */
5737 if( cData
->chOpts
[chAutomode
] == 'm')
5739 if(mn
->modes
& MODE_VOICE
)
5741 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5742 changes
->args
[used
++].u
.member
= mn
;
5745 /* otherwise, make user they do have voice */
5746 else if(!(mn
->modes
& MODE_VOICE
))
5748 changes
->args
[used
].mode
= MODE_VOICE
;
5749 changes
->args
[used
++].u
.member
= mn
;
5752 else /* They arnt on the userlist.. */
5754 /* If we voice everyone, but they dont.. */
5755 if(cData
->chOpts
[chAutomode
] == 'v')
5757 /* Remove anything except v */
5758 if(mn
->modes
& ~MODE_VOICE
)
5760 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5761 changes
->args
[used
++].u
.member
= mn
;
5764 if(!(mn
->modes
& MODE_VOICE
))
5766 changes
->args
[used
].mode
= MODE_VOICE
;
5767 changes
->args
[used
++].u
.member
= mn
;
5770 /* If we hop everyone, but they dont.. */
5771 else if(cData
->chOpts
[chAutomode
] == 'h')
5773 /* Remove anything except h */
5774 if(mn
->modes
& ~MODE_HALFOP
)
5776 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5777 changes
->args
[used
++].u
.member
= mn
;
5780 if(!(mn
->modes
& MODE_HALFOP
))
5782 changes
->args
[used
].mode
= MODE_HALFOP
;
5783 changes
->args
[used
++].u
.member
= mn
;
5786 /* If we op everyone, but they dont.. */
5787 else if(cData
->chOpts
[chAutomode
] == 'o')
5789 /* Remove anything except h */
5790 if(mn
->modes
& ~MODE_CHANOP
)
5792 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5793 changes
->args
[used
++].u
.member
= mn
;
5796 if(!(mn
->modes
& MODE_CHANOP
))
5798 changes
->args
[used
].mode
= MODE_CHANOP
;
5799 changes
->args
[used
++].u
.member
= mn
;
5802 /* they have no excuse for having modes, de-everything them */
5807 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5808 changes
->args
[used
++].u
.member
= mn
;
5814 changes
->argc
= used
;
5815 mod_chanmode_announce(chanserv
, channel
, changes
);
5816 mod_chanmode_free(changes
);
5819 static CHANSERV_FUNC(cmd_resync
)
5821 resync_channel(channel
);
5822 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5826 static CHANSERV_FUNC(cmd_seen
)
5828 struct userData
*uData
;
5829 struct handle_info
*handle
;
5830 char seen
[INTERVALLEN
];
5834 if(!irccasecmp(argv
[1], chanserv
->nick
))
5836 reply("CSMSG_IS_CHANSERV");
5840 if(!(handle
= get_handle_info(argv
[1])))
5842 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5846 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5848 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5853 reply("CSMSG_USER_PRESENT", handle
->handle
);
5854 else if(uData
->seen
)
5855 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5857 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5859 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5860 reply("CSMSG_USER_VACATION", handle
->handle
);
5865 static MODCMD_FUNC(cmd_names
)
5867 struct userNode
*targ
;
5868 struct userData
*targData
;
5869 unsigned int ii
, pos
;
5872 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5874 targ
= channel
->members
.list
[ii
]->user
;
5875 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5878 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5881 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5885 if(IsUserSuspended(targData
))
5887 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5890 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5891 reply("CSMSG_END_NAMES", channel
->name
);
5896 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5898 switch(ntype
->visible_type
)
5900 case NOTE_VIS_ALL
: return 1;
5901 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5902 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5907 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5909 struct userData
*uData
;
5911 switch(ntype
->set_access_type
)
5913 case NOTE_SET_CHANNEL_ACCESS
:
5914 if(!user
->handle_info
)
5916 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5918 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5919 case NOTE_SET_CHANNEL_SETTER
:
5920 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5921 case NOTE_SET_PRIVILEGED
: default:
5922 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5926 static CHANSERV_FUNC(cmd_note
)
5928 struct chanData
*cData
;
5930 struct note_type
*ntype
;
5932 cData
= channel
->channel_info
;
5935 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5939 /* If no arguments, show all visible notes for the channel. */
5945 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5947 note
= iter_data(it
);
5948 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5951 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5952 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5955 reply("CSMSG_NOTELIST_END", channel
->name
);
5957 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5959 /* If one argument, show the named note. */
5962 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5963 && note_type_visible_to_user(cData
, note
->type
, user
))
5965 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5967 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5968 && note_type_visible_to_user(NULL
, ntype
, user
))
5970 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5975 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5979 /* Assume they're trying to set a note. */
5983 ntype
= dict_find(note_types
, argv
[1], NULL
);
5986 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5989 else if(note_type_settable_by_user(channel
, ntype
, user
))
5991 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5992 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5993 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5994 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5995 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5997 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5999 /* The note is viewable to staff only, so return 0
6000 to keep the invocation from getting logged (or
6001 regular users can see it in !events). */
6007 reply("CSMSG_NO_ACCESS");
6014 static CHANSERV_FUNC(cmd_delnote
)
6019 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
6020 || !note_type_settable_by_user(channel
, note
->type
, user
))
6022 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
6025 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
6026 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
6030 static CHANSERV_FUNC(cmd_last
)
6036 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
6038 if(numoflines
< 1 || numoflines
> 200)
6040 reply("CSMSG_LAST_INVALID");
6043 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
6047 static CHANSERV_FUNC(cmd_events
)
6049 struct logSearch discrim
;
6050 struct logReport report
;
6051 unsigned int matches
, limit
;
6053 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
6054 if(limit
< 1 || limit
> 200)
6057 memset(&discrim
, 0, sizeof(discrim
));
6058 discrim
.masks
.bot
= chanserv
;
6059 discrim
.masks
.channel_name
= channel
->name
;
6061 discrim
.masks
.command
= argv
[2];
6062 discrim
.limit
= limit
;
6063 discrim
.max_time
= INT_MAX
;
6064 discrim
.severities
= 1 << LOG_COMMAND
;
6065 report
.reporter
= chanserv
;
6067 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
6068 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6070 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
6072 reply("MSG_MATCH_COUNT", matches
);
6074 reply("MSG_NO_MATCHES");
6078 static CHANSERV_FUNC(cmd_say
)
6084 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6085 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
6087 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6089 struct handle_info
*hi
;
6090 struct userNode
*authed
;
6093 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6095 if (!(hi
= get_handle_info(argv
[1] + 1)))
6097 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6101 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6102 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "%s", msg
);
6104 else if(GetUserH(argv
[1]))
6107 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6108 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
6112 reply("MSG_NOT_TARGET_NAME");
6118 static CHANSERV_FUNC(cmd_emote
)
6124 /* CTCP is so annoying. */
6125 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6126 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6128 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6130 struct handle_info
*hi
;
6131 struct userNode
*authed
;
6134 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6136 if (!(hi
= get_handle_info(argv
[1] + 1)))
6138 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6142 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6143 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6145 else if(GetUserH(argv
[1]))
6147 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6148 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6152 reply("MSG_NOT_TARGET_NAME");
6158 struct channelList
*
6159 chanserv_support_channels(void)
6161 return &chanserv_conf
.support_channels
;
6164 static CHANSERV_FUNC(cmd_expire
)
6166 int channel_count
= registered_channels
;
6167 expire_channels(NULL
);
6168 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
6173 chanserv_expire_suspension(void *data
)
6175 struct suspended
*suspended
= data
;
6176 struct chanNode
*channel
;
6179 /* Update the channel registration data structure. */
6180 if(!suspended
->expires
|| (now
< suspended
->expires
))
6181 suspended
->revoked
= now
;
6182 channel
= suspended
->cData
->channel
;
6183 suspended
->cData
->channel
= channel
;
6184 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
6186 /* If appropriate, re-join ChanServ to the channel. */
6187 if(!IsOffChannel(suspended
->cData
))
6189 spamserv_cs_suspend(channel
, 0, 0, NULL
);
6190 ss_cs_join_channel(channel
, 1);
6193 /* Mark everyone currently in the channel as present. */
6194 for(ii
= 0; ii
< channel
->members
.used
; ++ii
)
6196 struct userData
*uData
= GetChannelAccess(suspended
->cData
, channel
->members
.list
[ii
]->user
->handle_info
);
6205 static CHANSERV_FUNC(cmd_csuspend
)
6207 struct suspended
*suspended
;
6208 char reason
[MAXLEN
];
6209 time_t expiry
, duration
;
6210 struct userData
*uData
;
6214 if(IsProtected(channel
->channel_info
))
6216 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
6220 if(argv
[1][0] == '!')
6222 else if(IsSuspended(channel
->channel_info
))
6224 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
6225 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
6229 if(!strcmp(argv
[1], "0"))
6231 else if((duration
= ParseInterval(argv
[1])))
6232 expiry
= now
+ duration
;
6235 reply("MSG_INVALID_DURATION", argv
[1]);
6239 unsplit_string(argv
+ 2, argc
- 2, reason
);
6241 suspended
= calloc(1, sizeof(*suspended
));
6242 suspended
->revoked
= 0;
6243 suspended
->issued
= now
;
6244 suspended
->suspender
= strdup(user
->handle_info
->handle
);
6245 suspended
->expires
= expiry
;
6246 suspended
->reason
= strdup(reason
);
6247 suspended
->cData
= channel
->channel_info
;
6248 suspended
->previous
= suspended
->cData
->suspended
;
6249 suspended
->cData
->suspended
= suspended
;
6251 if(suspended
->expires
)
6252 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6254 if(IsSuspended(channel
->channel_info
))
6256 suspended
->previous
->revoked
= now
;
6257 if(suspended
->previous
->expires
)
6258 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
6260 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
6261 channel
->name
, suspended
->suspender
);
6265 /* Mark all users in channel as absent. */
6266 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
6275 /* Mark the channel as suspended, then part. */
6276 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
6277 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
6278 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
6279 reply("CSMSG_SUSPENDED", channel
->name
);
6280 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
6281 channel
->name
, suspended
->suspender
);
6286 static CHANSERV_FUNC(cmd_cunsuspend
)
6288 struct suspended
*suspended
;
6290 if(!IsSuspended(channel
->channel_info
))
6292 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
6296 suspended
= channel
->channel_info
->suspended
;
6298 /* Expire the suspension and join ChanServ to the channel. */
6299 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
6300 chanserv_expire_suspension(suspended
);
6301 reply("CSMSG_UNSUSPENDED", channel
->name
);
6302 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
6303 channel
->name
, user
->handle_info
->handle
);
6307 typedef struct chanservSearch
6315 unsigned long flags
;
6319 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
6322 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
6327 search
= malloc(sizeof(struct chanservSearch
));
6328 memset(search
, 0, sizeof(*search
));
6331 for(i
= 0; i
< argc
; i
++)
6333 /* Assume all criteria require arguments. */
6336 reply("MSG_MISSING_PARAMS", argv
[i
]);
6340 if(!irccasecmp(argv
[i
], "name"))
6341 search
->name
= argv
[++i
];
6342 else if(!irccasecmp(argv
[i
], "registrar"))
6343 search
->registrar
= argv
[++i
];
6344 else if(!irccasecmp(argv
[i
], "unvisited"))
6345 search
->unvisited
= ParseInterval(argv
[++i
]);
6346 else if(!irccasecmp(argv
[i
], "registered"))
6347 search
->registered
= ParseInterval(argv
[++i
]);
6348 else if(!irccasecmp(argv
[i
], "flags"))
6351 if(!irccasecmp(argv
[i
], "nodelete"))
6352 search
->flags
|= CHANNEL_NODELETE
;
6353 else if(!irccasecmp(argv
[i
], "suspended"))
6354 search
->flags
|= CHANNEL_SUSPENDED
;
6355 else if(!irccasecmp(argv
[i
], "unreviewed"))
6356 search
->flags
|= CHANNEL_UNREVIEWED
;
6359 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
6363 else if(!irccasecmp(argv
[i
], "limit"))
6364 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
6367 reply("MSG_INVALID_CRITERIA", argv
[i
]);
6372 if(search
->name
&& !strcmp(search
->name
, "*"))
6374 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
6375 search
->registrar
= 0;
6384 chanserv_channel_match(struct chanData
*channel
, search_t search
)
6386 const char *name
= channel
->channel
->name
;
6387 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
6388 (search
->registrar
&& !channel
->registrar
) ||
6389 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
6390 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
6391 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
6392 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
6399 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
6401 struct chanData
*channel
;
6402 unsigned int matches
= 0;
6404 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
6406 if(!chanserv_channel_match(channel
, search
))
6416 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
6421 search_print(struct chanData
*channel
, void *data
)
6423 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
6426 static CHANSERV_FUNC(cmd_search
)
6429 unsigned int matches
;
6430 channel_search_func action
;
6434 if(!irccasecmp(argv
[1], "count"))
6435 action
= search_count
;
6436 else if(!irccasecmp(argv
[1], "print"))
6437 action
= search_print
;
6440 reply("CSMSG_ACTION_INVALID", argv
[1]);
6444 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
6448 if(action
== search_count
)
6449 search
->limit
= INT_MAX
;
6451 if(action
== search_print
)
6453 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6454 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6458 matches
= chanserv_channel_search(search
, action
, user
);
6461 reply("MSG_MATCH_COUNT", matches
);
6463 reply("MSG_NO_MATCHES");
6469 static CHANSERV_FUNC(cmd_unvisited
)
6471 struct chanData
*cData
;
6472 time_t interval
= chanserv_conf
.channel_expire_delay
;
6473 char buffer
[INTERVALLEN
];
6474 unsigned int limit
= 25, matches
= 0;
6478 interval
= ParseInterval(argv
[1]);
6480 limit
= atoi(argv
[2]);
6483 intervalString(buffer
, interval
, user
->handle_info
);
6484 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
6486 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
6488 if((now
- cData
->visited
) < interval
)
6491 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
6492 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
6499 static MODCMD_FUNC(chan_opt_unreviewed
)
6501 struct chanData
*cData
= channel
->channel_info
;
6502 int value
= (cData
->flags
& CHANNEL_UNREVIEWED
) ? 1 : 0;
6508 /* The two directions can have different ACLs. */
6509 if(enabled_string(argv
[1]))
6511 else if(disabled_string(argv
[1]))
6515 reply("MSG_INVALID_BINARY", argv
[1]);
6519 if (new_value
!= value
)
6521 struct svccmd
*subcmd
;
6522 char subcmd_name
[32];
6524 snprintf(subcmd_name
, sizeof(subcmd_name
), "%s %s", argv
[0], (new_value
? "on" : "off"));
6525 subcmd
= dict_find(cmd
->parent
->commands
, subcmd_name
, NULL
);
6528 reply("MSG_COMMAND_DISABLED", subcmd_name
);
6531 else if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
6535 cData
->flags
|= CHANNEL_UNREVIEWED
;
6538 free(cData
->registrar
);
6539 cData
->registrar
= strdup(user
->handle_info
->handle
);
6540 cData
->flags
&= ~CHANNEL_UNREVIEWED
;
6547 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_ON"));
6549 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_OFF"));
6553 static MODCMD_FUNC(chan_opt_defaulttopic
)
6559 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6561 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6565 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6567 free(channel
->channel_info
->topic
);
6568 if(topic
[0] == '*' && topic
[1] == 0)
6570 topic
= channel
->channel_info
->topic
= NULL
;
6574 topic
= channel
->channel_info
->topic
= strdup(topic
);
6575 if(channel
->channel_info
->topic_mask
6576 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6577 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6579 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6582 if(channel
->channel_info
->topic
)
6583 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6585 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6589 static MODCMD_FUNC(chan_opt_topicmask
)
6593 struct chanData
*cData
= channel
->channel_info
;
6596 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6598 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6602 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6604 if(cData
->topic_mask
)
6605 free(cData
->topic_mask
);
6606 if(mask
[0] == '*' && mask
[1] == 0)
6608 cData
->topic_mask
= 0;
6612 cData
->topic_mask
= strdup(mask
);
6614 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6615 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6616 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6620 if(channel
->channel_info
->topic_mask
)
6621 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6623 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6627 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6631 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6635 if(greeting
[0] == '*' && greeting
[1] == 0)
6639 unsigned int length
= strlen(greeting
);
6640 if(length
> chanserv_conf
.greeting_length
)
6642 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6645 *data
= strdup(greeting
);
6654 reply(name
, user_find_message(user
, "MSG_NONE"));
6658 static MODCMD_FUNC(chan_opt_greeting
)
6660 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6663 static MODCMD_FUNC(chan_opt_usergreeting
)
6665 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6668 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6670 unsigned int charmax
;
6673 charmax
= atoi(argv
[1]);
6674 if ((charmax
> 0) && (charmax
<= chanserv_conf
.max_userinfo_length
))
6675 channel
->channel_info
->maxsetinfo
= charmax
;
6678 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6682 static MODCMD_FUNC(chan_opt_modes
)
6684 struct mod_chanmode
*new_modes
;
6685 char modes
[MODELEN
];
6689 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6690 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6694 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6696 reply("CSMSG_NO_ACCESS");
6699 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6701 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6703 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6705 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6708 else if(new_modes
->argc
> 1)
6710 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6711 mod_chanmode_free(new_modes
);
6716 channel
->channel_info
->modes
= *new_modes
;
6717 modcmd_chanmode_announce(new_modes
);
6718 mod_chanmode_free(new_modes
);
6722 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6724 reply("CSMSG_SET_MODES", modes
);
6726 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6730 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6732 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6734 struct chanData
*cData
= channel
->channel_info
;
6739 /* Set flag according to value. */
6740 if(enabled_string(argv
[1]))
6742 cData
->flags
|= mask
;
6745 else if(disabled_string(argv
[1]))
6747 cData
->flags
&= ~mask
;
6752 reply("MSG_INVALID_BINARY", argv
[1]);
6758 /* Find current option value. */
6759 value
= (cData
->flags
& mask
) ? 1 : 0;
6763 reply(name
, user_find_message(user
, "MSG_ON"));
6765 reply(name
, user_find_message(user
, "MSG_OFF"));
6769 static MODCMD_FUNC(chan_opt_nodelete
)
6771 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6773 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6777 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6780 static MODCMD_FUNC(chan_opt_dynlimit
)
6782 struct mod_chanmode change
;
6785 if (disabled_string(argv
[1])) {
6786 mod_chanmode_init(&change
);
6787 change
.modes_clear
|= MODE_LIMIT
;
6788 mod_chanmode_announce(chanserv
, channel
, &change
);
6792 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6795 static MODCMD_FUNC(chan_opt_offchannel
)
6797 struct chanData
*cData
= channel
->channel_info
;
6802 /* Set flag according to value. */
6803 if(enabled_string(argv
[1]))
6805 if(!IsOffChannel(cData
))
6806 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6807 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6810 else if(disabled_string(argv
[1]))
6812 if(IsOffChannel(cData
))
6814 struct mod_chanmode change
;
6815 mod_chanmode_init(&change
);
6817 change
.args
[0].mode
= MODE_CHANOP
;
6818 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6819 mod_chanmode_announce(chanserv
, channel
, &change
);
6821 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6826 reply("MSG_INVALID_BINARY", argv
[1]);
6832 /* Find current option value. */
6833 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6837 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6839 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6843 static MODCMD_FUNC(chan_opt_defaults
)
6845 struct userData
*uData
;
6846 struct chanData
*cData
;
6847 const char *confirm
;
6848 enum levelOption lvlOpt
;
6849 enum charOption chOpt
;
6851 cData
= channel
->channel_info
;
6852 uData
= GetChannelUser(cData
, user
->handle_info
);
6853 if(!uData
|| (uData
->access
< UL_OWNER
))
6855 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6858 confirm
= make_confirmation_string(uData
);
6859 if((argc
< 2) || strcmp(argv
[1], confirm
))
6861 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6864 cData
->flags
= (CHANNEL_DEFAULT_FLAGS
& ~CHANNEL_PRESERVED_FLAGS
)
6865 | (cData
->flags
& CHANNEL_PRESERVED_FLAGS
);
6866 cData
->modes
= chanserv_conf
.default_modes
;
6867 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6868 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6869 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6870 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6871 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6876 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6878 struct chanData
*cData
= channel
->channel_info
;
6879 struct userData
*uData
;
6880 unsigned short value
;
6884 if(!check_user_level(channel
, user
, option
, 1, 1))
6886 reply("CSMSG_CANNOT_SET");
6889 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6890 if(!value
&& strcmp(argv
[1], "0"))
6892 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6895 uData
= GetChannelUser(cData
, user
->handle_info
);
6896 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6898 reply("CSMSG_BAD_SETLEVEL");
6904 /* This test only applies to owners, since non-owners
6905 * trying to set an option to above their level get caught
6906 * by the CSMSG_BAD_SETLEVEL test above.
6908 if(value
> uData
->access
)
6910 reply("CSMSG_BAD_SETTERS");
6917 cData
->lvlOpts
[option
] = value
;
6919 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6923 static MODCMD_FUNC(chan_opt_enfops
)
6925 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6928 static MODCMD_FUNC(chan_opt_enfhalfops
)
6930 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6932 static MODCMD_FUNC(chan_opt_enfmodes
)
6934 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6937 static MODCMD_FUNC(chan_opt_enftopic
)
6939 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6942 static MODCMD_FUNC(chan_opt_pubcmd
)
6944 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6947 static MODCMD_FUNC(chan_opt_setters
)
6949 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6952 static MODCMD_FUNC(chan_opt_userinfo
)
6954 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6957 static MODCMD_FUNC(chan_opt_topicsnarf
)
6959 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6962 static MODCMD_FUNC(chan_opt_inviteme
)
6964 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6967 /* TODO: Make look like this when no args are
6969 * -X3- -------------------------------
6970 * -X3- BanTimeout: Bans are removed:
6971 * -X3- ----- * indicates current -----
6972 * -X3- 0: [*] Never.
6973 * -X3- 1: [ ] After 10 minutes.
6974 * -X3- 2: [ ] After 2 hours.
6975 * -X3- 3: [ ] After 4 hours.
6976 * -X3- 4: [ ] After 24 hours.
6977 * -X3- 5: [ ] After one week.
6978 * -X3- ------------- End -------------
6981 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6983 struct chanData
*cData
= channel
->channel_info
;
6984 int count
= charOptions
[option
].count
, idx
;
6988 idx
= atoi(argv
[1]);
6990 if(!isdigit(argv
[1][0]) || (idx
< 0) || (idx
>= count
))
6992 reply("CSMSG_INVALID_NUMERIC", idx
);
6993 /* Show possible values. */
6994 for(idx
= 0; idx
< count
; idx
++)
6995 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
6999 cData
->chOpts
[option
] = charOptions
[option
].values
[idx
].value
;
7003 /* Find current option value. */
7006 (idx
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[idx
].value
);
7010 /* Somehow, the option value is corrupt; reset it to the default. */
7011 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
7016 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
7020 static MODCMD_FUNC(chan_opt_automode
)
7022 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
7025 static MODCMD_FUNC(chan_opt_protect
)
7027 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
7030 static MODCMD_FUNC(chan_opt_toys
)
7032 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
7035 static MODCMD_FUNC(chan_opt_ctcpreaction
)
7037 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
7040 static MODCMD_FUNC(chan_opt_bantimeout
)
7042 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
7045 static MODCMD_FUNC(chan_opt_topicrefresh
)
7047 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
7050 static MODCMD_FUNC(chan_opt_resync
)
7052 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
7055 static struct svccmd_list set_shows_list
;
7058 handle_svccmd_unbind(struct svccmd
*target
, UNUSED_ARG(void *extra
)) {
7060 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
7061 if(target
== set_shows_list
.list
[ii
])
7062 set_shows_list
.used
= 0;
7065 static CHANSERV_FUNC(cmd_set
)
7067 struct svccmd
*subcmd
;
7071 /* Check if we need to (re-)initialize set_shows_list. */
7072 if(!set_shows_list
.used
)
7074 if(!set_shows_list
.size
)
7076 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
7077 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
7079 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
7081 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
7082 sprintf(buf
, "%s %s", argv
[0], name
);
7083 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7086 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
7089 svccmd_list_append(&set_shows_list
, subcmd
);
7095 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
7096 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
7098 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
7100 subcmd
= set_shows_list
.list
[ii
];
7101 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
7103 reply("CSMSG_CHANNEL_OPTIONS_END");
7107 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7108 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7111 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7114 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
7116 reply("CSMSG_NO_ACCESS");
7122 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7126 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
7128 struct userData
*uData
;
7130 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7133 reply("CSMSG_NOT_USER", channel
->name
);
7139 /* Just show current option value. */
7141 else if(enabled_string(argv
[1]))
7143 uData
->flags
|= mask
;
7145 else if(disabled_string(argv
[1]))
7147 uData
->flags
&= ~mask
;
7151 reply("MSG_INVALID_BINARY", argv
[1]);
7155 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
7159 static MODCMD_FUNC(user_opt_autoop
)
7161 struct userData
*uData
;
7163 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7166 reply("CSMSG_NOT_USER", channel
->name
);
7169 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
7170 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
7172 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
7175 static MODCMD_FUNC(user_opt_autoinvite
)
7177 if((argc
> 1) && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
7179 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
7181 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
7184 static MODCMD_FUNC(user_opt_autojoin
)
7186 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
7189 static MODCMD_FUNC(user_opt_info
)
7191 struct userData
*uData
;
7194 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7198 /* If they got past the command restrictions (which require access)
7199 * but fail this test, we have some fool with security override on.
7201 reply("CSMSG_NOT_USER", channel
->name
);
7208 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
7209 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
7211 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
7214 bp
= strcspn(infoline
, "\001");
7217 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
7222 if(infoline
[0] == '*' && infoline
[1] == 0)
7225 uData
->info
= strdup(infoline
);
7228 reply("CSMSG_USET_INFO", uData
->info
);
7230 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
7234 struct svccmd_list uset_shows_list
;
7236 static CHANSERV_FUNC(cmd_uset
)
7238 struct svccmd
*subcmd
;
7242 /* Check if we need to (re-)initialize uset_shows_list. */
7243 if(!uset_shows_list
.used
)
7247 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7250 if(!uset_shows_list
.size
)
7252 uset_shows_list
.size
= ArrayLength(options
);
7253 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
7255 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
7257 const char *name
= options
[ii
];
7258 sprintf(buf
, "%s %s", argv
[0], name
);
7259 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7262 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
7265 svccmd_list_append(&uset_shows_list
, subcmd
);
7271 /* Do this so options are presented in a consistent order. */
7272 reply("CSMSG_USER_OPTIONS");
7273 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
7274 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
7278 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7279 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7282 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7286 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7289 static CHANSERV_FUNC(cmd_giveownership
)
7291 struct handle_info
*new_owner_hi
;
7292 struct userData
*new_owner
;
7293 struct userData
*curr_user
;
7294 struct userData
*invoker
;
7295 struct chanData
*cData
= channel
->channel_info
;
7296 struct do_not_register
*dnr
;
7297 struct giveownership
*giveownership
;
7298 const char *confirm
;
7299 unsigned int force
, override
;
7300 unsigned short co_access
, new_owner_old_access
;
7301 char transfer_reason
[MAXLEN
];
7304 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
7305 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
7307 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
7308 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
7309 && (uData
->access
> 500)
7310 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
7311 || uData
->access
< 500));
7314 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
7316 struct userData
*owner
= NULL
;
7317 for(curr_user
= channel
->channel_info
->users
;
7319 curr_user
= curr_user
->next
)
7321 if(curr_user
->access
!= UL_OWNER
)
7325 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
7332 else if(!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
7334 char delay
[INTERVALLEN
];
7335 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
7336 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
7340 reply("CSMSG_NO_OWNER", channel
->name
);
7343 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
7345 if(new_owner_hi
== user
->handle_info
)
7347 reply("CSMSG_NO_TRANSFER_SELF");
7350 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
7355 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_OWNER
- 1, 0, NULL
, 0);
7359 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
7363 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
7365 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
7368 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
7369 if(!IsHelping(user
))
7370 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
7372 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
7376 invoker
= GetChannelUser(cData
, user
->handle_info
);
7377 if(invoker
->access
<= UL_OWNER
)
7379 confirm
= make_confirmation_string(curr_user
);
7380 if((argc
< 3) || strcmp(argv
[2], confirm
))
7382 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi
->handle
, confirm
);
7387 new_owner_old_access
= new_owner
->access
;
7388 if(new_owner
->access
>= UL_COOWNER
)
7389 co_access
= new_owner
->access
;
7391 co_access
= UL_COOWNER
;
7392 new_owner
->access
= UL_OWNER
;
7394 curr_user
->access
= co_access
;
7395 cData
->ownerTransfer
= now
;
7397 giveownership
= calloc(1, sizeof(*giveownership
));
7398 giveownership
->issued
= now
;
7399 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
7400 giveownership
->target
= strdup(new_owner_hi
->handle
);
7401 giveownership
->target_access
= new_owner_old_access
;
7404 if(argc
> (2 + force
))
7406 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
7407 giveownership
->reason
= strdup(transfer_reason
);
7409 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
7412 giveownership
->previous
= channel
->channel_info
->giveownership
;
7413 channel
->channel_info
->giveownership
= giveownership
;
7415 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
7416 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
7417 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
7422 chanserv_expire_user_suspension(void *data
)
7424 struct userData
*target
= data
;
7426 target
->expires
= 0;
7427 target
->flags
&= ~USER_SUSPENDED
;
7430 static CHANSERV_FUNC(cmd_suspend
)
7432 struct handle_info
*hi
;
7433 struct userData
*actor
, *real_actor
, *target
;
7434 unsigned int override
= 0;
7438 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7439 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7440 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7441 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7443 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7446 if(target
->access
>= actor
->access
)
7448 reply("MSG_USER_OUTRANKED", hi
->handle
);
7451 if(target
->flags
& USER_SUSPENDED
)
7453 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
7458 target
->present
= 0;
7461 if(!strcmp(argv
[2], "0"))
7465 unsigned int duration
;
7466 if(!(duration
= ParseInterval(argv
[2])))
7468 reply("MSG_INVALID_DURATION", argv
[2]);
7471 expiry
= now
+ duration
;
7474 target
->expires
= expiry
;
7477 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
7479 if(!real_actor
|| target
->access
>= real_actor
->access
)
7480 override
= CMD_LOG_OVERRIDE
;
7481 target
->flags
|= USER_SUSPENDED
;
7482 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
7483 return 1 | override
;
7486 static CHANSERV_FUNC(cmd_unsuspend
)
7488 struct handle_info
*hi
;
7489 struct userData
*actor
= NULL
, *real_actor
= NULL
, *target
;
7490 unsigned int override
= 0;
7493 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7494 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7495 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7496 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7498 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7501 if(target
->access
>= actor
->access
)
7503 reply("MSG_USER_OUTRANKED", hi
->handle
);
7506 if(!(target
->flags
& USER_SUSPENDED
))
7508 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
7511 if(!real_actor
|| target
->access
>= real_actor
->access
)
7512 override
= CMD_LOG_OVERRIDE
;
7513 target
->flags
&= ~USER_SUSPENDED
;
7514 scan_user_presence(target
, NULL
);
7515 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
7516 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
7517 return 1 | override
;
7520 static MODCMD_FUNC(cmd_deleteme
)
7522 struct handle_info
*hi
;
7523 struct userData
*target
;
7524 const char *confirm_string
;
7525 unsigned short access
;
7528 hi
= user
->handle_info
;
7529 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7531 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7534 if(target
->access
== UL_OWNER
)
7536 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
7539 confirm_string
= make_confirmation_string(target
);
7540 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
7542 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
7545 access
= target
->access
;
7546 channel_name
= strdup(channel
->name
);
7547 del_channel_user(target
, 1);
7548 reply("CSMSG_DELETED_YOU", access
, channel_name
);
7554 chanserv_refresh_topics(UNUSED_ARG(void *data
))
7556 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7557 struct chanData
*cData
;
7560 for(cData
= channelList
; cData
; cData
= cData
->next
)
7562 if(IsSuspended(cData
))
7564 opt
= cData
->chOpts
[chTopicRefresh
];
7567 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
7570 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
7571 cData
->last_refresh
= refresh_num
;
7573 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
7577 chanserv_auto_resync(UNUSED_ARG(void *data
))
7579 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7580 struct chanData
*cData
;
7583 for(cData
= channelList
; cData
; cData
= cData
->next
)
7585 if(IsSuspended(cData
)) continue;
7586 opt
= cData
->chOpts
[chResync
];
7587 if(opt
== 'n') continue;
7588 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
7589 resync_channel(cData
->channel
);
7590 cData
->last_resync
= refresh_num
;
7592 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7595 static CHANSERV_FUNC(cmd_unf
)
7599 char response
[MAXLEN
];
7600 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7601 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7602 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7605 reply("CSMSG_UNF_RESPONSE");
7609 static CHANSERV_FUNC(cmd_ping
)
7613 char response
[MAXLEN
];
7614 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7615 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7616 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7619 reply("CSMSG_PING_RESPONSE");
7623 static CHANSERV_FUNC(cmd_wut
)
7627 char response
[MAXLEN
];
7628 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7629 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7630 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7633 reply("CSMSG_WUT_RESPONSE");
7637 static CHANSERV_FUNC(cmd_roulette
)
7640 struct chanData
*cData
= channel
->channel_info
;
7643 if (cData
->roulette_chamber
) {
7644 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7648 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7649 cData
->roulette_chamber
= 1 + rand() % 6;
7655 static CHANSERV_FUNC(cmd_shoot
)
7658 struct chanData
*cData
= channel
->channel_info
;
7660 if (cData
->roulette_chamber
<= 0) {
7661 struct service
*service
;
7662 if ((service
= service_find(chanserv
->nick
))) {
7663 reply("CSMSG_ROULETTE_NEW", service
->trigger
);
7668 cData
->roulette_chamber
--;
7670 if (cData
->roulette_chamber
== 0) {
7671 reply("CSMSG_ROULETTE_BANG");
7672 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7673 DelUser(user
, chanserv
, 1, "BANG!!!!");
7675 reply("CSMSG_ROULETTE_CLICK");
7682 chanserv_remove_abuse(void *data
)
7684 char *remnick
= data
;
7685 struct userNode
*user
;
7686 /* sometimes the clone was killed and maybe even the user took their nick back
7687 * (ie, an oper) so dont kill them here after all unless they are local. */
7688 if( (user
= GetUserH(remnick
)) )
7690 DelUser(user
, NULL
, 1, "");
7693 int lamepart(struct userNode
*nick
) {
7694 struct modeNode
*mn
;
7695 unsigned int count
, n
;
7697 for (n
=count
=0; n
<nick
->channels
.used
; n
++) {
7698 mn
= nick
->channels
.list
[n
];
7699 irc_svspart(chanserv
, nick
, mn
->channel
);
7705 static CHANSERV_FUNC(cmd_spin
)
7710 int type
= 0, lamep
= 1;
7713 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7721 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7722 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7723 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7725 if(chanserv_conf
.wheel
->used
< 1) {
7726 /* wheel actions not defined! eek */
7730 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7731 if(!wheel
&& *wheel
)
7734 /* enable this to be able to manually specify a result for testing:
7735 log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
7741 /* connection reset by peer */
7742 if (!strcasecmp(wheel
, "peer")) {
7743 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7745 irc_kill(chanserv
, user
, "Connection reset by peer");
7747 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7749 /* part all channels */
7750 else if (!strcasecmp(wheel
, "partall")) {
7751 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7755 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7757 /* random time gline */
7758 else if (!strcasecmp(wheel
, "gline")) {
7759 char target
[HOSTLEN
+ 3];
7760 int wtime
= 120 + rand() % 600;
7762 strcpy(target
, "*@");
7763 strcat(target
, user
->hostname
);
7764 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7766 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1, 0);
7767 // irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7770 else if (!strcasecmp(wheel
, "shun")) {
7771 char target
[HOSTLEN
+ 3];
7772 int wtime
= 120 + rand() % 600;
7774 strcpy(target
, "*@");
7775 strcat(target
, user
->hostname
);
7776 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SHUN");
7778 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1);
7780 /* absolutely nothing */
7781 else if (!strcasecmp(wheel
, "nothing")) {
7782 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7784 /* join random chans and part em several times */
7785 else if (!strcasecmp(wheel
, "randjoin")) {
7791 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7792 while(complete
!= 1) {
7793 if (rndchans
!= 15) {
7794 chango
= 120 + rand() % 600;
7795 sputsock("%s SJ %s #%d "FMT_TIME_T
, self
->numeric
, user
->numeric
, chango
, now
);
7798 if (roundz0r
!= 1) {
7802 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7809 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7815 /* abuse line added to /whois */
7816 else if (!strcasecmp(wheel
, "abusewhois")) {
7817 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7818 irc_swhois(chanserv
, user
, "is being defecated on by services");
7820 /* kick from each channel your in */
7821 else if (!strcasecmp(wheel
, "kickall")) {
7822 unsigned int count
, n
;
7823 struct modeNode
*mn
;
7825 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7827 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7828 mn
= user
->channels
.list
[n
];
7829 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7832 /* random nick change */
7833 else if (!strcasecmp(wheel
, "nickchange")) {
7834 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7836 char *oldnick
= NULL
;
7837 char *oldident
= NULL
;
7838 char abusednick
[NICKLEN
] = "";
7839 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7840 struct userNode
*clone
;
7842 oldnick
= strdup(user
->nick
);
7843 oldident
= strdup(user
->ident
);
7845 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7847 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7848 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7849 if(!GetUserH(abusednick
))
7853 SVSNickChange(user
, abusednick
);
7854 irc_svsnick(chanserv
, user
, abusednick
);
7855 clone
= AddLocalUser(oldnick
, oldident
, "abused.by.wheel.of.misfortune", "I got abused by the wheel of misfortune :D", "+i");
7856 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7859 else if (!strcasecmp(wheel
, "kill")) {
7860 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7862 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7863 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7865 /* service ignore */
7866 else if (!strcasecmp(wheel
, "svsignore")) {
7867 int gagged
, ignoretime
= 0;
7868 char target
[HOSTLEN
+ 13];
7871 /* we cant gag opers, so just verbally abuse them */
7872 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7875 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7877 strcpy(target
, "*!*@");
7878 strcat(target
, user
->hostname
);
7879 ignoretime
= now
+ (1 + rand() % 120);
7881 gagged
= gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7883 /* kick and ban from each channel your in */
7884 else if (!strcasecmp(wheel
, "kickbanall")) {
7885 unsigned int count
, n
;
7886 struct modeNode
*mn
;
7887 //char ban[HOSTLEN + 1];
7889 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7891 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7892 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7893 struct mod_chanmode
*change
;
7894 /* struct banData *bData; */
7895 unsigned int exists
;
7896 /* int duration = 300; */
7899 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7901 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7902 mn
= user
->channels
.list
[n
];
7903 if(mn
->channel
->banlist
.used
>= MAXBANS
) {
7904 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7909 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7911 change
= mod_chanmode_alloc(1);
7912 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7913 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7916 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7917 mod_chanmode_free(change
);
7919 exists
= ChannelBanExists(mn
->channel
, ban
);
7921 change
= mod_chanmode_alloc(1);
7922 change
->args
[0].mode
= MODE_BAN
;
7923 change
->args
[0].u
.hostmask
= ban
;
7925 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7926 mod_chanmode_free(change
);
7930 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7934 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7938 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7945 static CHANSERV_FUNC(cmd_8ball
)
7947 unsigned int i
, j
, accum
;
7952 for(i
=1; i
<argc
; i
++)
7953 for(j
=0; argv
[i
][j
]; j
++)
7954 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7955 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7958 char response
[MAXLEN
];
7959 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7960 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7963 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7967 #else /* Use cool 8ball instead */
7969 void eightball(char *outcome
, int method
, unsigned int seed
)
7973 #define NUMOFCOLORS 18
7974 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7975 "white", "black", "grey", "brown",
7976 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7977 "fuchsia","turquoise","magenta", "cyan"};
7978 #define NUMOFLOCATIONS 50
7979 char balllocations
[50][55] = {
7980 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7981 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7982 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7983 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7984 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7985 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7986 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7987 "your bra", "your hair", "your bed", "the couch", "the wall",
7988 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7989 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7990 #define NUMOFPREPS 15
7991 char ballpreps
[50][50] = {
7992 "Near", "Somewhere near", "In", "In", "In",
7993 "In", "Hiding in", "Under", "Next to", "Over",
7994 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7995 #define NUMOFNUMS 34
7996 char ballnums
[50][50] = {
7997 "A hundred", "A thousand", "A few", "42",
7998 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7999 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8000 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8002 #define NUMOFMULTS 8
8003 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
8006 * 0: normal (Not used in x3)
8013 if (method
== 1) /* A Color */
8017 answer
= (rand() % 12); /* Make sure this is the # of entries */
8020 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
8022 case 1: strcpy(tmp
, "Sort of a light %s color.");
8024 case 2: strcpy(tmp
, "Dark and dreary %s.");
8026 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
8028 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
8030 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
8032 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
8034 case 10: strcpy(tmp
, "Solid %s.");
8036 case 11: strcpy(tmp
, "Transparent %s.");
8038 default: strcpy(outcome
, "An invalid random number was generated.");
8041 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
8044 else if (method
== 2) /* Location */
8046 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
8048 else if (method
== 3) /* Number of ___ */
8050 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
8054 //Debug(DBGWARNING, "Error in 8ball.");
8059 static CHANSERV_FUNC(cmd_8ball
)
8061 char *word1
, *word2
, *word3
;
8062 static char eb
[MAXLEN
];
8063 unsigned int accum
, i
, j
;
8067 for(i
=1; i
<argc
; i
++)
8068 for(j
=0; argv
[i
][j
]; j
++)
8069 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
8071 accum
+= time(NULL
)/3600;
8073 word2
= argc
>2?argv
[2]:"";
8074 word3
= argc
>3?argv
[3]:"";
8077 if((word2
) && strcasecmp(word1
, "what") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8078 eightball(eb
, 1, accum
);
8079 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8080 eightball(eb
, 1, accum
);
8081 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8082 eightball(eb
, 1, accum
);
8083 /*** LOCATION *****/
8088 (strcasecmp(word1
, "where") == 0) &&
8089 (strcasecmp(word2
, "is") == 0)
8093 strcasecmp(word1
, "where's") == 0
8096 eightball(eb
, 2, accum
);
8098 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
8099 eightball(eb
, 3, accum
);
8103 /* Generic 8ball question.. so pull from x3.conf srvx style */
8106 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
8109 char response
[MAXLEN
];
8110 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
8111 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8114 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
8120 char response
[MAXLEN
];
8121 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
8122 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8125 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
8130 static CHANSERV_FUNC(cmd_d
)
8132 unsigned long sides
, count
, modifier
, ii
, total
;
8133 char response
[MAXLEN
], *sep
;
8137 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
8147 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
8148 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
8152 else if((sep
[0] == '-') && isdigit(sep
[1]))
8153 modifier
= strtoul(sep
, NULL
, 10);
8154 else if((sep
[0] == '+') && isdigit(sep
[1]))
8155 modifier
= strtoul(sep
+1, NULL
, 10);
8162 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
8167 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
8170 for(total
= ii
= 0; ii
< count
; ++ii
)
8171 total
+= (rand() % sides
) + 1;
8174 if((count
> 1) || modifier
)
8176 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
8177 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
8181 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
8182 sprintf(response
, fmt
, total
, sides
);
8185 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8187 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8191 static CHANSERV_FUNC(cmd_huggle
)
8193 /* CTCP must be via PRIVMSG, never notice */
8195 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
8197 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
8201 static CHANSERV_FUNC(cmd_calc
)
8203 char response
[MAXLEN
];
8206 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8209 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8211 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8215 static CHANSERV_FUNC(cmd_reply
)
8219 unsplit_string(argv
+ 1, argc
- 1, NULL
);
8222 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8224 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
8229 chanserv_adjust_limit(void *data
)
8231 struct mod_chanmode change
;
8232 struct chanData
*cData
= data
;
8233 struct chanNode
*channel
= cData
->channel
;
8236 if(IsSuspended(cData
))
8239 cData
->limitAdjusted
= now
;
8240 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
8241 if(cData
->modes
.modes_set
& MODE_LIMIT
)
8243 if(limit
> cData
->modes
.new_limit
)
8244 limit
= cData
->modes
.new_limit
;
8245 else if(limit
== cData
->modes
.new_limit
)
8249 mod_chanmode_init(&change
);
8250 change
.modes_set
= MODE_LIMIT
;
8251 change
.new_limit
= limit
;
8252 mod_chanmode_announce(chanserv
, channel
, &change
);
8256 handle_new_channel(struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8258 struct chanData
*cData
;
8260 if(!(cData
= channel
->channel_info
))
8263 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
8264 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
8266 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
8267 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
8271 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
8273 struct banData
*bData
;
8274 struct mod_chanmode
*change
;
8276 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
8281 if (chan
->channel_info
) {
8282 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
8284 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0))
8296 check_bans(struct userNode
*user
, const char *channel
)
8298 struct chanNode
*chan
;
8299 struct mod_chanmode change
;
8300 struct chanData
*cData
;
8301 struct banData
*bData
;
8303 if (!(chan
= GetChannel(channel
)))
8306 if(!(cData
= chan
->channel_info
))
8309 mod_chanmode_init(&change
);
8312 if(chan
->banlist
.used
< MAXBANS
)
8314 /* Not joining through a ban. */
8315 for(bData
= cData
->bans
;
8316 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8317 bData
= bData
->next
);
8321 char kick_reason
[MAXLEN
];
8322 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8324 bData
->triggered
= now
;
8325 if(bData
!= cData
->bans
)
8327 /* Shuffle the ban to the head of the list. */
8329 bData
->next
->prev
= bData
->prev
;
8331 bData
->prev
->next
= bData
->next
;
8334 bData
->next
= cData
->bans
;
8337 cData
->bans
->prev
= bData
;
8339 cData
->bans
= bData
;
8342 change
.args
[0].mode
= MODE_BAN
;
8343 change
.args
[0].u
.hostmask
= bData
->mask
;
8344 mod_chanmode_announce(chanserv
, chan
, &change
);
8345 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8353 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
8356 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
8358 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
, 0))
8365 /* Welcome to my worst nightmare. Warning: Read (or modify)
8366 the code below at your own risk. */
8368 handle_join(struct modeNode
*mNode
, UNUSED_ARG(void *extra
))
8370 struct mod_chanmode change
;
8371 struct userNode
*user
= mNode
->user
;
8372 struct chanNode
*channel
= mNode
->channel
;
8373 struct chanData
*cData
;
8374 struct userData
*uData
= NULL
;
8375 struct banData
*bData
;
8376 struct handle_info
*handle
;
8377 unsigned int modes
= 0, info
= 0;
8380 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8383 cData
= channel
->channel_info
;
8384 if(channel
->members
.used
> cData
->max
)
8385 cData
->max
= channel
->members
.used
;
8388 /* Check for bans. If they're joining through a ban, one of two
8390 * 1: Join during a netburst, by riding the break. Kick them
8391 * unless they have ops or voice in the channel.
8392 * 2: They're allowed to join through the ban (an invite in
8393 * ircu2.10, or a +e on Hybrid, or something).
8394 * If they're not joining through a ban, and the banlist is not
8395 * full, see if they're on the banlist for the channel. If so,
8398 if(user
->uplink
->burst
&& !mNode
->modes
)
8401 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
8403 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
, 0))
8405 /* Riding a netburst. Naughty. */
8406 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
8413 if(user
->handle_info
)
8415 handle
= user
->handle_info
;
8418 uData
= GetTrueChannelAccess(cData
, handle
);
8423 mod_chanmode_init(&change
);
8426 /* TODO: maybe only people above inviteme level? -Rubin */
8427 /* We don't kick people with access */
8428 if(!uData
&& !channel_user_is_exempt(user
, channel
))
8430 if(channel
->banlist
.used
< MAXBANS
)
8432 /* Not joining through a ban. */
8433 for(bData
= cData
->bans
;
8434 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8435 bData
= bData
->next
);
8439 char kick_reason
[MAXLEN
];
8440 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8442 bData
->triggered
= now
;
8443 if(bData
!= cData
->bans
)
8445 /* Shuffle the ban to the head of the list. */
8447 bData
->next
->prev
= bData
->prev
;
8449 bData
->prev
->next
= bData
->next
;
8452 bData
->next
= cData
->bans
;
8455 cData
->bans
->prev
= bData
;
8456 cData
->bans
= bData
;
8459 change
.args
[0].mode
= MODE_BAN
;
8460 change
.args
[0].u
.hostmask
= bData
->mask
;
8461 mod_chanmode_announce(chanserv
, channel
, &change
);
8462 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8468 /* ChanServ will not modify the limits in join-flooded channels.
8469 It will also skip DynLimit processing when the user (or srvx)
8470 is bursting in, because there are likely more incoming. */
8471 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8472 && !user
->uplink
->burst
8473 && !channel
->join_flooded
8474 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
8476 /* The user count has begun "bumping" into the channel limit,
8477 so set a timer to raise the limit a bit. Any previous
8478 timers are removed so three incoming users within the delay
8479 results in one limit change, not three. */
8481 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8482 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8485 /* Give automodes exept during join-floods */
8486 if(!channel
->join_flooded
)
8488 if(cData
->chOpts
[chAutomode
] == 'v')
8489 modes
|= MODE_VOICE
;
8490 else if(cData
->chOpts
[chAutomode
] == 'h')
8491 modes
|= MODE_HALFOP
;
8492 else if(cData
->chOpts
[chAutomode
] == 'o')
8493 modes
|= MODE_CHANOP
;
8496 greeting
= cData
->greeting
;
8497 if(user
->handle_info
)
8499 /* handle = user->handle_info; */
8501 if(IsHelper(user
) && !IsHelping(user
))
8504 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8506 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
8508 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8514 /* uData = GetTrueChannelAccess(cData, handle); */
8515 if(uData
&& !IsUserSuspended(uData
))
8517 /* non users getting automodes are handled above. */
8518 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
8520 /* just op everyone with access */
8521 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8522 modes
|= MODE_VOICE
;
8523 /* or do their access level */
8524 else if(uData
->access
>= UL_OP
)
8525 modes
|= MODE_CHANOP
;
8526 else if(uData
->access
>= UL_HALFOP
)
8527 modes
|= MODE_HALFOP
;
8528 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
8529 modes
|= MODE_VOICE
;
8531 if(uData
->access
>= UL_PRESENT
)
8532 cData
->visited
= now
;
8533 if(cData
->user_greeting
)
8534 greeting
= cData
->user_greeting
;
8536 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
8537 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
8545 /* If user joining normally (not during burst), apply op or voice,
8546 * and send greeting/userinfo as appropriate.
8548 if(!user
->uplink
->burst
)
8552 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8553 if(modes & MODE_CHANOP) {
8554 modes &= ~MODE_HALFOP;
8555 modes &= ~MODE_VOICE;
8558 change
.args
[0].mode
= modes
;
8559 change
.args
[0].u
.member
= mNode
;
8560 mod_chanmode_announce(chanserv
, channel
, &change
);
8563 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
8565 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
8571 chanserv_autojoin_channels(struct userNode
*user
)
8573 struct userData
*channel
;
8575 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8577 struct chanNode
*cn
;
8578 struct modeNode
*mn
;
8580 if(IsUserSuspended(channel
)
8581 || IsSuspended(channel
->channel
)
8582 || !(cn
= channel
->channel
->channel
))
8585 mn
= GetUserMode(cn
, user
);
8588 if(!IsUserSuspended(channel
)
8589 && IsUserAutoJoin(channel
)
8590 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8592 && !user
->uplink
->burst
)
8593 irc_svsjoin(chanserv
, user
, cn
);
8599 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
), UNUSED_ARG(void *extra
))
8601 struct mod_chanmode change
;
8602 struct userData
*channel
;
8603 unsigned int ii
, jj
, i
;
8605 if(!user
->handle_info
)
8608 mod_chanmode_init(&change
);
8610 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8612 struct chanNode
*cn
;
8613 struct chanData
*cData
;
8614 struct modeNode
*mn
;
8615 if(IsUserSuspended(channel
)
8616 || IsSuspended(channel
->channel
)
8617 || !(cn
= channel
->channel
->channel
))
8620 cData
= cn
->channel_info
;
8621 mn
= GetUserMode(cn
, user
);
8624 if(!IsUserSuspended(channel
)
8625 && IsUserAutoInvite(channel
)
8626 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8628 && !user
->uplink
->burst
)
8629 irc_invite(chanserv
, user
, cn
);
8633 if(channel
->access
>= UL_PRESENT
)
8634 channel
->channel
->visited
= now
;
8636 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8638 if(channel
->access
>= UL_OP
)
8639 change
.args
[0].mode
= MODE_CHANOP
;
8640 else if(channel
->access
>= UL_HALFOP
)
8641 change
.args
[0].mode
= MODE_HALFOP
;
8642 else if(channel
->access
>= UL_PEON
)
8643 change
.args
[0].mode
= MODE_VOICE
;
8645 change
.args
[0].mode
= 0;
8646 change
.args
[0].u
.member
= mn
;
8647 if(change
.args
[0].mode
)
8648 mod_chanmode_announce(chanserv
, cn
, &change
);
8651 channel
->seen
= now
;
8652 channel
->present
= 1;
8655 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8657 struct chanNode
*chan
= user
->channels
.list
[ii
]->channel
;
8658 struct banData
*ban
;
8660 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8661 || !chan
->channel_info
8662 || IsSuspended(chan
->channel_info
))
8664 if(protect_user(user
, chanserv
, chan
->channel_info
, true))
8666 for(jj
= 0; jj
< chan
->banlist
.used
; ++jj
)
8667 if(user_matches_glob(user
, chan
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8669 if(jj
< chan
->banlist
.used
)
8671 for(ban
= chan
->channel_info
->bans
; ban
; ban
= ban
->next
)
8673 char kick_reason
[MAXLEN
];
8674 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
, 0))
8676 change
.args
[0].mode
= MODE_BAN
;
8677 change
.args
[0].u
.hostmask
= ban
->mask
;
8678 mod_chanmode_announce(chanserv
, chan
, &change
);
8679 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8680 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8681 ban
->triggered
= now
;
8686 if(IsSupportHelper(user
))
8688 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8690 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8692 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8698 if (user
->handle_info
->ignores
->used
) {
8699 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8700 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8704 if (user
->handle_info
->epithet
)
8705 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8707 /* process autojoin channels 5 seconds later as this sometimes
8708 happens before autohide */
8709 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8710 chanserv_autojoin_channels(user
);
8714 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
), UNUSED_ARG(void *extra
))
8716 struct chanData
*cData
;
8717 struct userData
*uData
;
8719 cData
= mn
->channel
->channel_info
;
8720 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8723 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8725 /* Allow for a bit of padding so that the limit doesn't
8726 track the user count exactly, which could get annoying. */
8727 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8729 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8730 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8734 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8736 scan_user_presence(uData
, mn
->user
);
8738 if (uData
->access
>= UL_PRESENT
)
8739 cData
->visited
= now
;
8742 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8745 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
) {
8746 struct chanNode
*channel
;
8747 struct userNode
*exclude
;
8748 /* When looking at the channel that is being /part'ed, we
8749 * have to skip over the client that is leaving. For
8750 * other channels, we must not do that.
8752 channel
= chanserv_conf
.support_channels
.list
[ii
];
8753 exclude
= (channel
== mn
->channel
) ? mn
->user
: NULL
;
8754 if(find_handle_in_channel(channel
, mn
->user
->handle_info
, exclude
))
8757 if(ii
== chanserv_conf
.support_channels
.used
)
8758 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8763 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8765 struct userData
*uData
;
8767 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8768 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8769 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8772 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8774 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8775 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8778 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8783 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
, UNUSED_ARG(void *extra
))
8785 struct chanData
*cData
;
8787 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8790 cData
= channel
->channel_info
;
8791 if(bad_topic(channel
, user
, channel
->topic
))
8792 { /* User doesnt have privs to set topics. Undo it */
8793 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8794 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8797 /* If there is a topic mask set, and the new topic doesnt match,
8798 * set the topic to mask + new_topic */
8799 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8801 char new_topic
[TOPICLEN
+1];
8802 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8805 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8806 /* and fall through to topicsnarf code below.. */
8808 else /* Topic couldnt fit into mask, was too long */
8810 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8811 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8812 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8816 /* With topicsnarf, grab the topic and save it as the default topic. */
8817 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8820 cData
->topic
= strdup(channel
->topic
);
8826 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
, UNUSED_ARG(void *extra
))
8828 struct mod_chanmode
*bounce
= NULL
;
8829 unsigned int bnc
, ii
;
8832 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8835 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8836 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8838 char correct
[MAXLEN
];
8839 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8840 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8841 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8843 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8845 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8847 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8848 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8851 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8854 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8855 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8856 if(bounce
->args
[bnc
].u
.member
)
8860 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8861 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8863 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8865 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8867 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8868 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8871 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8872 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8873 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8876 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8878 const char *ban
= change
->args
[ii
].u
.hostmask
;
8879 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8882 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8883 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8884 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8886 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8891 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8892 mod_chanmode_announce(chanserv
, channel
, bounce
);
8893 for(ii
= 0; ii
< change
->argc
; ++ii
)
8894 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8895 free((char*)bounce
->args
[ii
].u
.hostmask
);
8896 mod_chanmode_free(bounce
);
8901 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
), UNUSED_ARG(void *extra
))
8903 struct chanNode
*channel
;
8904 struct banData
*bData
;
8905 struct mod_chanmode change
;
8906 unsigned int ii
, jj
;
8907 char kick_reason
[MAXLEN
];
8909 mod_chanmode_init(&change
);
8911 change
.args
[0].mode
= MODE_BAN
;
8912 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8914 channel
= user
->channels
.list
[ii
]->channel
;
8915 /* Need not check for bans if they're opped or voiced. */
8916 /* TODO: does this make sense in automode v, h, and o? *
8917 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8918 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8920 /* Need not check for bans unless channel registration is active. */
8921 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8923 /* Look for a matching ban already on the channel. */
8924 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8925 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8927 /* Need not act if we found one. */
8928 if(jj
< channel
->banlist
.used
)
8930 /* don't kick someone on the userlist */
8931 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8933 /* Look for a matching ban in this channel. */
8934 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8936 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
8938 change
.args
[0].u
.hostmask
= bData
->mask
;
8939 mod_chanmode_announce(chanserv
, channel
, &change
);
8940 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8941 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8942 bData
->triggered
= now
;
8943 break; /* we don't need to check any more bans in the channel */
8948 static void handle_rename(struct handle_info
*handle
, const char *old_handle
, UNUSED_ARG(void *extra
))
8950 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8954 dict_remove2(handle_dnrs
, old_handle
, 1);
8955 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8956 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8961 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
, UNUSED_ARG(void *extra
))
8963 struct userNode
*h_user
;
8965 if(handle
->channels
)
8967 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8968 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8970 while(handle
->channels
)
8971 del_channel_user(handle
->channels
, 1);
8976 handle_server_link(UNUSED_ARG(struct server
*server
), UNUSED_ARG(void *extra
))
8978 struct chanData
*cData
;
8980 for(cData
= channelList
; cData
; cData
= cData
->next
)
8982 if(!IsSuspended(cData
))
8983 cData
->may_opchan
= 1;
8984 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8985 && !cData
->channel
->join_flooded
8986 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8987 < chanserv_conf
.adjust_threshold
))
8989 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8990 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8997 chanserv_conf_read(void)
9001 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
9002 struct mod_chanmode
*change
;
9003 struct string_list
*strlist
;
9004 struct chanNode
*chan
;
9007 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
9009 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
9012 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9013 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9014 chanserv_conf
.support_channels
.used
= 0;
9015 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
9017 for(ii
= 0; ii
< strlist
->used
; ++ii
)
9019 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9022 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
9024 channelList_append(&chanserv_conf
.support_channels
, chan
);
9027 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
9030 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9033 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
9035 channelList_append(&chanserv_conf
.support_channels
, chan
);
9037 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
9038 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
9039 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
9040 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
9041 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
9042 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
9043 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
9044 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
9045 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
9046 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
9047 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
9048 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
9049 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
9050 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
9051 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
9052 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
9053 str
= database_get_data(conf_node
, KEY_DNR_EXPIRE_FREQ
, RECDB_QSTRING
);
9054 chanserv_conf
.dnr_expire_frequency
= str
? ParseInterval(str
) : 3600;
9055 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
9056 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
9057 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
9058 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
9059 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
9060 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
9061 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
9062 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
9063 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
9065 NickChange(chanserv
, str
, 0);
9066 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
9067 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
9068 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
9069 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
9070 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
9071 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
9072 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
9073 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
9074 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
9075 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
9076 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
9077 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
9078 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
9079 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
9080 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
9081 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
9082 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
9083 god_timeout
= str
? ParseInterval(str
) : 60*15;
9084 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
9087 safestrncpy(mode_line
, str
, sizeof(mode_line
));
9088 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
9089 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
9090 && (change
->argc
< 2))
9092 chanserv_conf
.default_modes
= *change
;
9093 mod_chanmode_free(change
);
9095 str
= database_get_data(conf_node
, KEY_VALID_CHANNEL_REGEX
, RECDB_QSTRING
);
9096 if (chanserv_conf
.valid_channel_regex_set
)
9097 regfree(&chanserv_conf
.valid_channel_regex
);
9099 int err
= regcomp(&chanserv_conf
.valid_channel_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
9100 chanserv_conf
.valid_channel_regex_set
= !err
;
9101 if (err
) log_module(CS_LOG
, LOG_ERROR
, "Bad valid_channel_regex (error %d)", err
);
9103 chanserv_conf
.valid_channel_regex_set
= 0;
9105 free_string_list(chanserv_conf
.wheel
);
9106 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
9108 strlist
= string_list_copy(strlist
);
9111 static const char *list
[] = {
9112 "peer", "partall", "gline", /* "shun", */
9113 "nothing", "randjoin", "abusewhois", "kickall",
9114 "nickchange", "kill", "svsignore", "kickbanall",
9117 strlist
= alloc_string_list(ArrayLength(list
)-1);
9118 for(ii
=0; list
[ii
]; ii
++)
9119 string_list_append(strlist
, strdup(list
[ii
]));
9121 chanserv_conf
.wheel
= strlist
;
9123 free_string_list(chanserv_conf
.set_shows
);
9124 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
9126 strlist
= string_list_copy(strlist
);
9129 static const char *list
[] = {
9130 /* free form text */
9131 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9132 /* options based on user level */
9133 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9134 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
9135 /* multiple choice options */
9136 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
9137 /* binary options */
9138 "DynLimit", "NoDelete", "BanTimeout",
9142 strlist
= alloc_string_list(ArrayLength(list
)-1);
9143 for(ii
=0; list
[ii
]; ii
++)
9144 string_list_append(strlist
, strdup(list
[ii
]));
9146 chanserv_conf
.set_shows
= strlist
;
9147 /* We don't look things up now, in case the list refers to options
9148 * defined by modules initialized after this point. Just mark the
9149 * function list as invalid, so it will be initialized.
9151 set_shows_list
.used
= 0;
9153 free_string_list(chanserv_conf
.eightball
);
9154 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
9157 strlist
= string_list_copy(strlist
);
9161 strlist
= alloc_string_list(4);
9162 string_list_append(strlist
, strdup("Yes."));
9163 string_list_append(strlist
, strdup("No."));
9164 string_list_append(strlist
, strdup("Maybe so."));
9166 chanserv_conf
.eightball
= strlist
;
9168 free_string_list(chanserv_conf
.old_ban_names
);
9169 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
9171 strlist
= string_list_copy(strlist
);
9173 strlist
= alloc_string_list(2);
9174 chanserv_conf
.old_ban_names
= strlist
;
9175 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
9176 off_channel
= str
? atoi(str
) : 0;
9180 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
9183 struct note_type
*ntype
;
9186 if(!(obj
= GET_RECORD_OBJECT(rd
)))
9188 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
9191 if(!(ntype
= chanserv_create_note_type(key
)))
9193 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
9197 /* Figure out set access */
9198 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
9200 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9201 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
9203 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
9205 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
9206 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
9208 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
9210 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
9214 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
9215 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9216 ntype
->set_access
.min_opserv
= 0;
9219 /* Figure out visibility */
9220 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
9221 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9222 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
9223 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9224 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
9225 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
9226 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
9227 ntype
->visible_type
= NOTE_VIS_ALL
;
9229 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9231 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
9232 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
9236 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9238 struct handle_info
*handle
;
9239 struct userData
*uData
;
9240 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
9242 unsigned short access_level
, lastaccess
= 0;
9244 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9246 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
9250 access_level
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
9251 if(access_level
> UL_OWNER
)
9253 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
9257 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
9258 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
9259 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
9260 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
9261 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9262 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
9263 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
9264 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
9265 lastaccess
= lstacc
? atoi(lstacc
) : 0;
9267 handle
= get_handle_info(key
);
9270 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
9274 uData
= add_channel_user(chan
, handle
, access_level
, last_seen
, inf
, 0);
9275 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
9276 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
9278 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
9279 if (uData
->accessexpiry
> 0)
9280 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
9282 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
9283 if (uData
->clvlexpiry
> 0)
9284 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
9286 uData
->lastaccess
= lastaccess
;
9288 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
9290 if(uData
->expires
> now
)
9291 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
9293 uData
->flags
&= ~USER_SUSPENDED
;
9296 /* Upgrade: set autoop to the inverse of noautoop */
9297 if(chanserv_read_version
< 2)
9299 /* if noautoop is true, set autoop false, and vice versa */
9300 if(uData
->flags
& USER_NOAUTO_OP
)
9301 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
9303 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
9304 log_module(CS_LOG
, LOG_INFO
, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version
, uData
->flags
, key
, chan
->channel
->name
);
9310 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9312 struct banData
*bData
;
9313 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
9314 time_t set_time
, triggered_time
, expires_time
;
9316 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9318 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
9322 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
9323 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
9324 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
9325 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9326 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
9327 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
9328 if (!reason
|| !owner
)
9331 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
9332 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
9334 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
9336 expires_time
= set_time
+ atoi(s_duration
);
9340 if(!reason
|| (expires_time
&& (expires_time
< now
)))
9343 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
9346 static struct suspended
*
9347 chanserv_read_suspended(dict_t obj
)
9349 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
9353 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
9354 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9355 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
9356 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9357 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9358 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9359 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
9360 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
9361 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9362 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
9366 static struct giveownership
*
9367 chanserv_read_giveownership(dict_t obj
)
9369 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
9373 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
9374 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
9376 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
9378 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
9379 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
9381 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
9382 giveownership
->reason
= str
? strdup(str
) : NULL
;
9383 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9384 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9386 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9387 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
9388 return giveownership
;
9392 chanserv_channel_read(const char *key
, struct record_data
*hir
)
9394 struct suspended
*suspended
;
9395 struct giveownership
*giveownership
;
9396 struct mod_chanmode
*modes
;
9397 struct chanNode
*cNode
;
9398 struct chanData
*cData
;
9399 struct dict
*channel
, *obj
;
9400 char *str
, *argv
[10];
9404 channel
= hir
->d
.object
;
9406 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
9409 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
9412 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
9415 cData
= register_channel(cNode
, str
);
9418 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
9422 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
9424 enum levelOption lvlOpt
;
9425 enum charOption chOpt
;
9427 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
9428 cData
->flags
= atoi(str
);
9430 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9432 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
9434 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
9435 else if(levelOptions
[lvlOpt
].old_flag
)
9437 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9438 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
9440 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
9444 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9446 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
9448 cData
->chOpts
[chOpt
] = str
[0];
9451 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
9453 enum levelOption lvlOpt
;
9454 enum charOption chOpt
;
9457 cData
->flags
= base64toint(str
, 5);
9458 count
= strlen(str
+= 5);
9459 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9462 if(levelOptions
[lvlOpt
].old_flag
)
9464 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9465 lvl
= levelOptions
[lvlOpt
].flag_value
;
9467 lvl
= levelOptions
[lvlOpt
].default_value
;
9469 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
9471 case 'c': lvl
= UL_COOWNER
; break;
9472 case 'm': lvl
= UL_MANAGER
; break;
9473 case 'n': lvl
= UL_OWNER
+1; break;
9474 case 'o': lvl
= UL_OP
; break;
9475 case 'p': lvl
= UL_PEON
; break;
9476 case 'h': lvl
= UL_HALFOP
; break;
9477 case 'w': lvl
= UL_OWNER
; break;
9478 default: lvl
= 0; break;
9480 cData
->lvlOpts
[lvlOpt
] = lvl
;
9482 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9483 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
9486 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
9488 suspended
= chanserv_read_suspended(obj
);
9489 cData
->suspended
= suspended
;
9490 suspended
->cData
= cData
;
9491 /* We could use suspended->expires and suspended->revoked to
9492 * set the CHANNEL_SUSPENDED flag, but we don't. */
9494 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
9496 suspended
= calloc(1, sizeof(*suspended
));
9497 suspended
->issued
= 0;
9498 suspended
->revoked
= 0;
9499 suspended
->suspender
= strdup(str
);
9500 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
9501 suspended
->expires
= str
? atoi(str
) : 0;
9502 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
9503 suspended
->reason
= strdup(str
? str
: "No reason");
9504 suspended
->previous
= NULL
;
9505 cData
->suspended
= suspended
;
9506 suspended
->cData
= cData
;
9510 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9511 suspended
= NULL
; /* to squelch a warning */
9514 if(IsSuspended(cData
)) {
9515 if(suspended
->expires
> now
)
9516 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
9517 else if(suspended
->expires
)
9518 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9521 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
9523 giveownership
= chanserv_read_giveownership(obj
);
9524 cData
->giveownership
= giveownership
;
9527 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
9528 struct mod_chanmode change
;
9529 mod_chanmode_init(&change
);
9531 change
.args
[0].mode
= MODE_CHANOP
;
9532 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
9533 mod_chanmode_announce(chanserv
, cNode
, &change
);
9536 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
9537 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9538 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
9539 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9540 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
9541 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9542 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
9543 cData
->max
= str
? atoi(str
) : 0;
9544 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
9545 cData
->greeting
= str
? strdup(str
) : NULL
;
9546 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
9547 cData
->user_greeting
= str
? strdup(str
) : NULL
;
9548 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
9549 cData
->topic_mask
= str
? strdup(str
) : NULL
;
9550 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
9551 cData
->topic
= str
? strdup(str
) : NULL
;
9553 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
9554 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
9556 if(!IsSuspended(cData
)
9557 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
9558 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
9559 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
9560 cData
->modes
= *modes
;
9562 cData
->modes
.modes_set
|= MODE_REGISTERED
;
9563 if(cData
->modes
.argc
> 1)
9564 cData
->modes
.argc
= 1;
9565 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
9566 mod_chanmode_free(modes
);
9569 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
9570 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9571 user_read_helper(iter_key(it
), iter_data(it
), cData
);
9573 if(!cData
->users
&& !IsProtected(cData
))
9575 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
9576 unregister_channel(cData
, "has empty user list.");
9580 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
9581 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9582 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
9584 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
9585 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9587 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
9588 struct record_data
*rd
= iter_data(it
);
9589 const char *note
, *setter
;
9591 if(rd
->type
!= RECDB_OBJECT
)
9593 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9597 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9599 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9601 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9605 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9606 if(!setter
) setter
= "<unknown>";
9607 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9615 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9617 const char *setter
, *reason
, *str
;
9618 struct do_not_register
*dnr
;
9621 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9624 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9627 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9630 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9633 str
= database_get_data(hir
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9634 expiry
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9635 if(expiry
&& expiry
<= now
)
9637 dnr
= chanserv_add_dnr(key
, setter
, expiry
, reason
);
9640 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9642 dnr
->set
= atoi(str
);
9648 chanserv_version_read(struct dict
*section
)
9652 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9654 chanserv_read_version
= atoi(str
);
9655 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9659 chanserv_saxdb_read(struct dict
*database
)
9661 struct dict
*section
;
9664 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9665 chanserv_version_read(section
);
9667 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9668 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9669 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9671 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9672 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9673 chanserv_channel_read(iter_key(it
), iter_data(it
));
9675 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9676 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9677 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9683 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9685 int high_present
= 0;
9686 saxdb_start_record(ctx
, KEY_USERS
, 1);
9687 for(; uData
; uData
= uData
->next
)
9689 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9691 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9692 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9693 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9694 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9695 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9696 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9698 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9700 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9702 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9703 saxdb_end_record(ctx
);
9705 saxdb_end_record(ctx
);
9706 return high_present
;
9710 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9714 saxdb_start_record(ctx
, KEY_BANS
, 1);
9715 for(; bData
; bData
= bData
->next
)
9717 saxdb_start_record(ctx
, bData
->mask
, 0);
9718 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9719 if(bData
->triggered
)
9720 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9722 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9724 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9726 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9727 saxdb_end_record(ctx
);
9729 saxdb_end_record(ctx
);
9733 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9735 saxdb_start_record(ctx
, name
, 0);
9736 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9737 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9739 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9741 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9743 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9745 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9746 saxdb_end_record(ctx
);
9750 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9752 saxdb_start_record(ctx
, name
, 0);
9753 if(giveownership
->staff_issuer
)
9754 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9755 if(giveownership
->old_owner
)
9756 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9757 if(giveownership
->target
)
9758 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9759 if(giveownership
->target_access
)
9760 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9761 if(giveownership
->reason
)
9762 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9763 if(giveownership
->issued
)
9764 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9765 if(giveownership
->previous
)
9766 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9767 saxdb_end_record(ctx
);
9771 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9775 enum levelOption lvlOpt
;
9776 enum charOption chOpt
;
9778 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9780 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9781 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9783 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9784 if(channel
->registrar
)
9785 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9786 if(channel
->greeting
)
9787 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9788 if(channel
->user_greeting
)
9789 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9790 if(channel
->topic_mask
)
9791 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9792 if(channel
->suspended
)
9793 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9794 if(channel
->giveownership
)
9795 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9797 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9798 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9799 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9800 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9801 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9803 buf
[0] = channel
->chOpts
[chOpt
];
9805 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9807 saxdb_end_record(ctx
);
9809 if (channel
->maxsetinfo
)
9810 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9812 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9814 mod_chanmode_format(&channel
->modes
, buf
);
9815 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9818 high_present
= chanserv_write_users(ctx
, channel
->users
);
9819 chanserv_write_bans(ctx
, channel
->bans
);
9821 if(dict_size(channel
->notes
))
9825 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9826 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9828 struct note
*note
= iter_data(it
);
9829 saxdb_start_record(ctx
, iter_key(it
), 0);
9830 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9831 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9832 saxdb_end_record(ctx
);
9834 saxdb_end_record(ctx
);
9837 if(channel
->ownerTransfer
)
9838 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9839 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9840 saxdb_end_record(ctx
);
9844 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9848 saxdb_start_record(ctx
, ntype
->name
, 0);
9849 switch(ntype
->set_access_type
)
9851 case NOTE_SET_CHANNEL_ACCESS
:
9852 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9854 case NOTE_SET_CHANNEL_SETTER
:
9855 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9857 case NOTE_SET_PRIVILEGED
: default:
9858 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9861 switch(ntype
->visible_type
)
9863 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9864 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9865 case NOTE_VIS_PRIVILEGED
:
9866 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9868 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9869 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9870 saxdb_end_record(ctx
);
9874 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9876 struct do_not_register
*dnr
;
9877 dict_iterator_t it
, next
;
9879 for(it
= dict_first(dnrs
); it
; it
= next
)
9881 next
= iter_next(it
);
9882 dnr
= iter_data(it
);
9883 if(dnr
->expires
&& dnr
->expires
<= now
)
9885 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9887 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9890 dict_remove(dnrs
, iter_key(it
));
9891 saxdb_write_int(ctx
, KEY_EXPIRES
, dnr
->expires
);
9893 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9894 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9895 saxdb_end_record(ctx
);
9900 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9903 struct chanData
*channel
;
9905 /* Version Control*/
9906 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9907 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9908 saxdb_end_record(ctx
);
9911 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9912 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9913 chanserv_write_note_type(ctx
, iter_data(it
));
9914 saxdb_end_record(ctx
);
9917 saxdb_start_record(ctx
, KEY_DNR
, 1);
9918 write_dnrs_helper(ctx
, handle_dnrs
);
9919 write_dnrs_helper(ctx
, plain_dnrs
);
9920 write_dnrs_helper(ctx
, mask_dnrs
);
9921 saxdb_end_record(ctx
);
9924 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9925 for(channel
= channelList
; channel
; channel
= channel
->next
)
9926 chanserv_write_channel(ctx
, channel
);
9927 saxdb_end_record(ctx
);
9933 chanserv_db_cleanup(UNUSED_ARG(void *extra
)) {
9935 unreg_part_func(handle_part
, NULL
);
9937 unregister_channel(channelList
, "terminating.");
9938 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9939 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9940 free(chanserv_conf
.support_channels
.list
);
9941 dict_delete(handle_dnrs
);
9942 dict_delete(plain_dnrs
);
9943 dict_delete(mask_dnrs
);
9944 dict_delete(note_types
);
9945 free_string_list(chanserv_conf
.eightball
);
9946 free_string_list(chanserv_conf
.old_ban_names
);
9947 free_string_list(chanserv_conf
.wheel
);
9948 free_string_list(chanserv_conf
.set_shows
);
9949 free(set_shows_list
.list
);
9950 free(uset_shows_list
.list
);
9953 struct userData
*helper
= helperList
;
9954 helperList
= helperList
->next
;
9959 #if defined(GCC_VARMACROS)
9960 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
9961 #elif defined(C99_VARMACROS)
9962 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
9964 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9965 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9968 init_chanserv(const char *nick
)
9970 struct chanNode
*chan
;
9973 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9974 conf_register_reload(chanserv_conf_read
);
9977 reg_server_link_func(handle_server_link
, NULL
);
9978 reg_new_channel_func(handle_new_channel
, NULL
);
9979 reg_join_func(handle_join
, NULL
);
9980 reg_part_func(handle_part
, NULL
);
9981 reg_kick_func(handle_kick
, NULL
);
9982 reg_topic_func(handle_topic
, NULL
);
9983 reg_mode_change_func(handle_mode
, NULL
);
9984 reg_nick_change_func(handle_nick_change
, NULL
);
9985 reg_auth_func(handle_auth
, NULL
);
9988 reg_handle_rename_func(handle_rename
, NULL
);
9989 reg_unreg_func(handle_unreg
, NULL
);
9991 handle_dnrs
= dict_new();
9992 dict_set_free_data(handle_dnrs
, free
);
9993 plain_dnrs
= dict_new();
9994 dict_set_free_data(plain_dnrs
, free
);
9995 mask_dnrs
= dict_new();
9996 dict_set_free_data(mask_dnrs
, free
);
9998 reg_svccmd_unbind_func(handle_svccmd_unbind
, NULL
);
9999 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
10000 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+channel", NULL
);
10001 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
10002 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
10003 DEFINE_COMMAND(dnrsearch
, 3, 0, "template", "noregister", NULL
);
10004 modcmd_register(chanserv_module
, "dnrsearch print", NULL
, 0, 0, NULL
);
10005 modcmd_register(chanserv_module
, "dnrsearch remove", NULL
, 0, 0, NULL
);
10006 modcmd_register(chanserv_module
, "dnrsearch count", NULL
, 0, 0, NULL
);
10007 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
10008 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
|MODCMD_IGNORE_CSUSPEND
, "flags", "+helping", NULL
);
10009 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
|MODCMD_IGNORE_CSUSPEND
, "flags", "+helping", NULL
);
10010 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
10011 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
10013 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
10015 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
10016 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
10018 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10019 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10020 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10021 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10022 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10024 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
10025 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
10026 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
10027 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10028 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10029 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10030 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10032 DEFINE_COMMAND(levels
, 1, 0, NULL
);
10034 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10035 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
10036 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10037 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
10039 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10040 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
10041 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10042 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10043 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10044 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10045 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10046 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10047 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10048 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10050 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10051 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10052 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10053 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
10054 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
10055 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
10056 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
10057 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
10058 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
10059 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
10060 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
10061 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
10062 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10063 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10065 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10066 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10067 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10068 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10070 /* if you change dellamer access, see also places
10071 * like unbanme which have manager hardcoded. */
10072 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10073 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
10075 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
10077 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
10079 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10080 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10081 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10082 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10083 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10084 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10085 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10086 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10087 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10088 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10089 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10090 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10092 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
10093 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
10095 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
10096 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
10097 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
10098 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
10100 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10101 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10102 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
10103 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
10104 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
10106 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10107 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10108 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10109 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10110 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10111 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10112 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10113 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10114 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10115 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10116 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
10118 /* Channel options */
10119 DEFINE_CHANNEL_OPTION(defaulttopic
);
10120 DEFINE_CHANNEL_OPTION(topicmask
);
10121 DEFINE_CHANNEL_OPTION(greeting
);
10122 DEFINE_CHANNEL_OPTION(usergreeting
);
10123 DEFINE_CHANNEL_OPTION(modes
);
10124 DEFINE_CHANNEL_OPTION(enfops
);
10125 DEFINE_CHANNEL_OPTION(enfhalfops
);
10126 DEFINE_CHANNEL_OPTION(automode
);
10127 DEFINE_CHANNEL_OPTION(protect
);
10128 DEFINE_CHANNEL_OPTION(enfmodes
);
10129 DEFINE_CHANNEL_OPTION(enftopic
);
10130 DEFINE_CHANNEL_OPTION(pubcmd
);
10131 DEFINE_CHANNEL_OPTION(userinfo
);
10132 DEFINE_CHANNEL_OPTION(dynlimit
);
10133 DEFINE_CHANNEL_OPTION(topicsnarf
);
10134 DEFINE_CHANNEL_OPTION(nodelete
);
10135 DEFINE_CHANNEL_OPTION(toys
);
10136 DEFINE_CHANNEL_OPTION(setters
);
10137 DEFINE_CHANNEL_OPTION(topicrefresh
);
10138 DEFINE_CHANNEL_OPTION(resync
);
10139 DEFINE_CHANNEL_OPTION(ctcpreaction
);
10140 DEFINE_CHANNEL_OPTION(bantimeout
);
10141 DEFINE_CHANNEL_OPTION(inviteme
);
10142 DEFINE_CHANNEL_OPTION(unreviewed
);
10143 modcmd_register(chanserv_module
, "set unreviewed on", NULL
, 0, 0, "flags", "+helping", NULL
);
10144 modcmd_register(chanserv_module
, "set unreviewed off", NULL
, 0, 0, "flags", "+oper", NULL
);
10145 DEFINE_CHANNEL_OPTION(maxsetinfo
);
10146 if(off_channel
> 1)
10147 DEFINE_CHANNEL_OPTION(offchannel
);
10148 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
10150 /* Alias set topic to set defaulttopic for compatibility. */
10151 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
10154 DEFINE_USER_OPTION(autoinvite
);
10155 DEFINE_USER_OPTION(autojoin
);
10156 DEFINE_USER_OPTION(info
);
10157 DEFINE_USER_OPTION(autoop
);
10159 /* Alias uset autovoice to uset autoop. */
10160 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
10162 note_types
= dict_new();
10163 dict_set_free_data(note_types
, chanserv_deref_note_type
);
10166 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
10167 chanserv
= AddLocalUser(nick
, nick
, NULL
, "Channel Services", modes
);
10168 service_register(chanserv
)->trigger
= '!';
10169 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
, NULL
);
10172 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
10174 if(chanserv_conf
.channel_expire_frequency
)
10175 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
10177 if(chanserv_conf
.dnr_expire_frequency
)
10178 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
10180 if(chanserv_conf
.ban_timeout_frequency
)
10181 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
10183 if(chanserv_conf
.refresh_period
)
10185 time_t next_refresh
;
10186 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
10187 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
10188 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
10191 if (autojoin_channels
&& chanserv
) {
10192 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
10193 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
10194 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
10198 reg_exit_func(chanserv_db_cleanup
, NULL
);
10199 message_register_table(msgtab
);