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;
3293 unsigned short access_level_user
= 0;
3298 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3299 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3301 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
3304 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3306 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3312 access_level
= user_level_from_name(argv
[1], UL_OWNER
);
3313 char *useraccess
= user_level_name_from_level(victim
->access
);
3314 access_level_user
= user_level_from_name(useraccess
, UL_OWNER
);
3317 reply("CSMSG_INVALID_ACCESS", argv
[1]);
3320 if(access_level
!= access_level_user
)
3322 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, useraccess
, argv
[1]);
3328 access_level
= victim
->access
;
3331 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
3333 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
3337 /* If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override */
3338 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& real_actor
!= victim
))
3339 override
= CMD_LOG_OVERRIDE
;
3341 chan_name
= strdup(channel
->name
);
3342 del_channel_user(victim
, 1);
3343 reply("CSMSG_DELETED_USER", handle
->handle
, access_level
, chan_name
);
3345 return 1 | override
;
3349 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
3351 struct userData
*actor
, *real_actor
, *uData
, *next
;
3352 unsigned int override
= 0;
3354 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3355 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3357 if(min_access
> max_access
)
3359 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3363 if((actor
->access
<= max_access
) && !IsHelping(user
))
3365 reply("CSMSG_NO_ACCESS");
3369 if(!real_actor
|| real_actor
->access
<= max_access
)
3370 override
= CMD_LOG_OVERRIDE
;
3372 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3376 if((uData
->access
>= min_access
)
3377 && (uData
->access
<= max_access
)
3378 && match_ircglob(uData
->handle
->handle
, mask
))
3379 del_channel_user(uData
, 1);
3382 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3383 return 1 | override
;
3386 static CHANSERV_FUNC(cmd_mdelowner
)
3388 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3391 static CHANSERV_FUNC(cmd_mdelcoowner
)
3393 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3396 static CHANSERV_FUNC(cmd_mdelmanager
)
3398 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3401 static CHANSERV_FUNC(cmd_mdelop
)
3403 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3406 static CHANSERV_FUNC(cmd_mdelhalfop
)
3408 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3411 static CHANSERV_FUNC(cmd_mdelpeon
)
3413 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3416 static CHANSERV_FUNC(cmd_mdelpal
)
3418 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3421 static CHANSERV_FUNC(cmd_levels
)
3423 struct helpfile_table tbl
;
3426 tbl
.length
= 6 + 1; // 6 levels
3429 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3430 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3431 tbl
.contents
[0][0] = "Level";
3432 tbl
.contents
[0][1] = "From";
3433 tbl
.contents
[0][2] = "-";
3434 tbl
.contents
[0][3] = "To";
3436 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3437 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3438 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3439 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3440 tbl
.contents
[ii
][3] = msnprintf(1, "");
3442 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3443 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3444 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3445 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3446 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3448 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3449 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3450 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3451 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3452 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3454 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3455 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3456 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3457 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3458 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3460 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3461 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3462 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3463 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3464 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3466 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3467 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3468 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3469 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3470 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3472 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3476 reply("CSMSG_LEVELS_HEADER");
3477 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3478 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3479 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3480 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3481 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3482 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3489 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3491 struct banData
*bData
, *next
;
3492 char interval
[INTERVALLEN
];
3497 limit
= now
- duration
;
3498 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3502 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3505 del_channel_ban(bData
);
3509 intervalString(interval
, duration
, user
->handle_info
);
3510 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3515 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
)
3517 struct userData
*actor
, *uData
, *next
;
3518 char interval
[INTERVALLEN
];
3522 actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3523 if(min_access
> max_access
)
3525 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3529 if(!actor
|| actor
->access
<= max_access
)
3531 reply("CSMSG_NO_ACCESS");
3536 limit
= now
- duration
;
3537 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3541 if((uData
->seen
> limit
)
3543 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3546 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3547 || (!max_access
&& (uData
->access
< actor
->access
)))
3549 del_channel_user(uData
, 1);
3557 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3559 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3563 static CHANSERV_FUNC(cmd_trim
)
3565 unsigned long duration
;
3566 unsigned short min_level
, max_level
;
3571 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3572 duration
= ParseInterval(argv
[2]);
3575 reply("CSMSG_CANNOT_TRIM");
3579 if(!irccasecmp(argv
[1], "lamers"))
3581 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3584 else if(!irccasecmp(argv
[1], "users"))
3586 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3589 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3591 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3594 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3596 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3601 reply("CSMSG_INVALID_TRIM", argv
[1]);
3606 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3607 to the user. cmd_all takes advantage of this. */
3608 static CHANSERV_FUNC(cmd_up
)
3610 struct mod_chanmode change
;
3611 struct userData
*uData
;
3614 mod_chanmode_init(&change
);
3616 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3617 if(!change
.args
[0].u
.member
)
3620 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3624 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3628 reply("CSMSG_GODMODE_UP", argv
[0]);
3631 else if(uData
->access
>= UL_OP
)
3633 change
.args
[0].mode
= MODE_CHANOP
;
3634 errmsg
= "CSMSG_ALREADY_OPPED";
3636 else if(uData
->access
>= UL_HALFOP
)
3638 change
.args
[0].mode
= MODE_HALFOP
;
3639 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3641 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3643 change
.args
[0].mode
= MODE_VOICE
;
3644 errmsg
= "CSMSG_ALREADY_VOICED";
3649 reply("CSMSG_NO_ACCESS");
3652 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3653 if(!change
.args
[0].mode
)
3656 reply(errmsg
, channel
->name
);
3659 modcmd_chanmode_announce(&change
);
3663 static CHANSERV_FUNC(cmd_down
)
3665 struct mod_chanmode change
;
3667 mod_chanmode_init(&change
);
3669 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3670 if(!change
.args
[0].u
.member
)
3673 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3677 if(!change
.args
[0].u
.member
->modes
)
3680 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3684 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3685 modcmd_chanmode_announce(&change
);
3689 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
)
3691 struct userData
*cList
;
3693 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3695 if(IsSuspended(cList
->channel
)
3696 || IsUserSuspended(cList
)
3697 || !GetUserMode(cList
->channel
->channel
, user
))
3700 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3706 static CHANSERV_FUNC(cmd_upall
)
3708 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3711 static CHANSERV_FUNC(cmd_downall
)
3713 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3716 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3717 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3720 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
)
3722 unsigned int ii
, valid
;
3723 struct userNode
*victim
;
3724 struct mod_chanmode
*change
;
3726 change
= mod_chanmode_alloc(argc
- 1);
3728 for(ii
=valid
=0; ++ii
< argc
; )
3730 if(!(victim
= GetUserH(argv
[ii
])))
3732 change
->args
[valid
].mode
= mode
;
3733 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3734 if(!change
->args
[valid
].u
.member
)
3736 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3741 change
->argc
= valid
;
3742 if(valid
< (argc
-1))
3743 reply("CSMSG_PROCESS_FAILED");
3746 modcmd_chanmode_announce(change
);
3747 reply(action
, channel
->name
);
3749 mod_chanmode_free(change
);
3753 static CHANSERV_FUNC(cmd_op
)
3755 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3758 static CHANSERV_FUNC(cmd_hop
)
3760 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3763 static CHANSERV_FUNC(cmd_deop
)
3765 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3768 static CHANSERV_FUNC(cmd_dehop
)
3770 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3773 static CHANSERV_FUNC(cmd_voice
)
3775 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3778 static CHANSERV_FUNC(cmd_devoice
)
3780 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3784 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3791 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3793 struct modeNode
*mn
= channel
->members
.list
[ii
];
3795 if(IsService(mn
->user
))
3798 b
= user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
3804 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3808 victims
[(*victimCount
)++] = mn
;
3813 int is_extban(char *b
) {
3821 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3823 struct userNode
*victim
;
3824 struct modeNode
**victims
;
3825 unsigned int offset
, n
, victimCount
, duration
= 0;
3827 char *reason
= "Bye.", *ban
, *name
;
3828 char interval
[INTERVALLEN
];
3830 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3831 REQUIRE_PARAMS(offset
);
3834 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3835 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3837 /* Truncate the reason to a length of TOPICLEN, as
3838 the ircd does; however, leave room for an ellipsis
3839 and the kicker's nick. */
3840 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3844 if((victim
= GetUserH(argv
[1])))
3846 victims
= alloca(sizeof(victims
[0]));
3847 victims
[0] = GetUserMode(channel
, victim
);
3848 /* XXX: The comparison with ACTION_KICK is just because all
3849 * other actions can work on users outside the channel, and we
3850 * want to allow those (e.g. unbans) in that case. If we add
3851 * some other ejection action for in-channel users, change
3853 victimCount
= victims
[0] ? 1 : 0;
3855 if(IsService(victim
))
3858 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3862 if((action
== ACTION_KICK
) && !victimCount
)
3865 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3869 if(protect_user(victim
, user
, channel
->channel_info
, false))
3871 // This translates to send_message(user, cmd->parent->bot, ...)
3872 // if user is x3 (ctcp action) cmd is null and segfault.
3874 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3878 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3879 name
= victim
->nick
;
3881 else if(!is_ircmask(argv
[1]) && (*argv
[1] == '*'))
3883 struct handle_info
*hi
;
3884 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
3885 const char *accountname
= argv
[1] + 1;
3887 if(!(hi
= get_handle_info(accountname
)))
3889 reply("MSG_HANDLE_UNKNOWN", accountname
);
3893 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", hi
->handle
);
3894 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3896 b
= bad_channel_ban(channel
, user
, banmask
, &victimCount
, victims
);
3899 reply("CSMSG_MASK_PROTECTED", banmask
);
3903 reply("CSMSG_BAD_BAN", banmask
);
3907 if((action
== ACTION_KICK
) && (victimCount
== 0))
3909 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, banmask
);
3913 name
= ban
= strdup(banmask
);
3917 if(!is_ircmask(argv
[1]))
3920 reply("MSG_NICK_UNKNOWN", argv
[1]);
3924 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3926 b
= bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
);
3927 if(cmd
&& (b
== 1)) {
3928 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3931 else if(cmd
&& (b
== -1)) {
3932 reply("CSMSG_BAD_BAN", argv
[1]);
3935 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3936 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3938 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3939 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3940 some creativity, but its not x3's job to be the ban censor anyway. */
3941 if(is_overmask(argv
[1]))
3944 reply("CSMSG_LAME_MASK", argv
[1]);
3947 //TODO: We have no support to do protection etc etc so for now we dont let you use x3 to set extended bans.
3948 if(is_extban(argv
[1]))
3951 reply("CSMSG_NO_EXTBANS", argv
[1]);
3955 if((action
== ACTION_KICK
) && (victimCount
== 0))
3958 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3962 name
= ban
= strdup(argv
[1]);
3965 /* Truncate the ban in place if necessary; we must ensure
3966 that 'ban' is a valid ban mask before sanitizing it. */
3968 sanitize_ircmask(ban
);
3970 if(action
& ACTION_ADD_LAMER
)
3972 struct banData
*bData
, *next
;
3974 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3977 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3982 if(action
& ACTION_ADD_TIMED_LAMER
)
3984 duration
= ParseInterval(argv
[2]);
3989 reply("CSMSG_DURATION_TOO_LOW");
3993 else if(duration
> (86400 * 365 * 2))
3996 reply("CSMSG_DURATION_TOO_HIGH");
4003 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
4005 if(match_ircglobs(bData
->mask
, ban
))
4007 int exact
= !irccasecmp(bData
->mask
, ban
);
4009 /* The ban is redundant; there is already a ban
4010 with the same effect in place. */
4014 free(bData
->reason
);
4015 bData
->reason
= strdup(reason
);
4016 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
4018 reply("CSMSG_REASON_CHANGE", ban
);
4022 if(exact
&& bData
->expires
)
4026 /* If the ban matches an existing one exactly,
4027 extend the expiration time if the provided
4028 duration is longer. */
4029 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
4031 bData
->expires
= now
+ duration
;
4042 /* Delete the expiration timeq entry and
4043 requeue if necessary. */
4044 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
4047 timeq_add(bData
->expires
, expire_ban
, bData
);
4051 /* automated kickban, dont reply */
4054 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
4056 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4062 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
4069 if(match_ircglobs(ban
, bData
->mask
))
4071 /* The ban we are adding makes previously existing
4072 bans redundant; silently remove them. */
4073 del_channel_ban(bData
);
4077 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
);
4079 name
= ban
= strdup(bData
->mask
);
4083 /* WHAT DOES THIS DO?? -Rubin */
4084 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
4086 extern const char *hidden_host_suffix
;
4087 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
4089 unsigned int l1
, l2
;
4092 l2
= strlen(old_name
);
4095 if(irccasecmp(ban
+ l1
- l2
, old_name
))
4097 new_mask
= malloc(MAXLEN
);
4098 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
4100 name
= ban
= new_mask
;
4105 if(action
& ACTION_BAN
)
4107 unsigned int exists
;
4108 struct mod_chanmode
*change
;
4110 if(channel
->banlist
.used
>= MAXBANS
)
4113 reply("CSMSG_BANLIST_FULL", channel
->name
);
4118 exists
= ChannelBanExists(channel
, ban
);
4119 change
= mod_chanmode_alloc(victimCount
+ 1);
4120 for(n
= 0; n
< victimCount
; ++n
)
4122 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
4123 change
->args
[n
].u
.member
= victims
[n
];
4127 change
->args
[n
].mode
= MODE_BAN
;
4128 change
->args
[n
++].u
.hostmask
= ban
;
4132 modcmd_chanmode_announce(change
);
4134 mod_chanmode_announce(chanserv
, channel
, change
);
4135 mod_chanmode_free(change
);
4137 if(exists
&& (action
== ACTION_BAN
))
4140 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
4146 if(action
& ACTION_ADD_LAMER
)
4148 char kick_reason
[MAXLEN
];
4149 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4151 for(n
= 0; n
< victimCount
; n
++) {
4152 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
4153 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4157 else if(action
& ACTION_KICK
)
4159 char kick_reason
[MAXLEN
];
4160 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4162 for(n
= 0; n
< victimCount
; n
++) {
4163 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4169 /* No response, since it was automated. */
4171 else if(action
& ACTION_ADD_LAMER
)
4174 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
4176 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4178 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
4179 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
4180 else if(action
& ACTION_BAN
)
4181 reply("CSMSG_BAN_DONE", name
, channel
->name
);
4182 else if(action
& ACTION_KICK
&& victimCount
)
4183 reply("CSMSG_KICK_DONE", name
, channel
->name
);
4189 static CHANSERV_FUNC(cmd_kickban
)
4191 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
4194 static CHANSERV_FUNC(cmd_kick
)
4196 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
4199 static CHANSERV_FUNC(cmd_ban
)
4201 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
4204 static CHANSERV_FUNC(cmd_addlamer
)
4206 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
4209 static CHANSERV_FUNC(cmd_addtimedlamer
)
4211 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
4214 static struct mod_chanmode
*
4215 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
4217 struct mod_chanmode
*change
;
4218 unsigned char *match
;
4219 unsigned int ii
, count
;
4221 match
= alloca(bans
->used
);
4224 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4226 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
4227 MATCH_USENICK
| MATCH_VISIBLE
, 0);
4234 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4236 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
4243 change
= mod_chanmode_alloc(count
);
4244 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4248 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
4249 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
4251 assert(count
== change
->argc
);
4255 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
4257 unsigned int jj
, ii
, count
;
4259 struct chanData
*channel
;
4261 struct mod_chanmode
*change
;
4263 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
4264 /* Walk through every channel */
4265 for(channel
= channelList
; channel
; channel
= channel
->next
) {
4266 switch(channel
->chOpts
[chBanTimeout
])
4268 default: case '0': continue; /* Dont remove bans in this chan */
4269 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
4270 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
4271 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
4272 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
4273 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
4276 /* First find out how many bans were going to unset */
4277 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4278 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see below
4279 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
&& !is_extban(channel
->channel
->banlist
.list
[jj
]->ban
))
4283 /* At least one ban, so setup a removal */
4284 change
= mod_chanmode_alloc(count
);
4286 /* Walk over every ban in this channel.. */
4287 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4288 bn
= channel
->channel
->banlist
.list
[jj
];
4289 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see above
4290 if (bn
->set
< bantimeout
&& !is_extban(bn
->ban
)) {
4291 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
4293 /* Add this ban to the mode change */
4294 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4295 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
4297 /* Pull this ban out of the list */
4298 banList_remove(&(channel
->channel
->banlist
), bn
);
4303 /* Send the modes to IRC */
4304 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
4306 /* free memory from strdup above */
4307 for(ii
= 0; ii
< count
; ++ii
)
4308 free((char*)change
->args
[ii
].u
.hostmask
);
4310 mod_chanmode_free(change
);
4313 /* Set this function to run again */
4314 if(chanserv_conf
.ban_timeout_frequency
)
4315 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
4320 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
4322 struct userNode
*actee
;
4328 /* may want to allow a comma delimited list of users... */
4329 if(!(actee
= GetUserH(argv
[1])))
4331 if(!is_ircmask(argv
[1]) && *argv
[1] == '*')
4333 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
4334 const char *accountname
= argv
[1] + 1;
4336 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", accountname
);
4337 mask
= strdup(banmask
);
4339 else if(!is_ircmask(argv
[1]))
4341 reply("MSG_NICK_UNKNOWN", argv
[1]);
4346 mask
= strdup(argv
[1]);
4350 /* We don't sanitize the mask here because ircu
4352 if(action
& ACTION_UNBAN
)
4354 struct mod_chanmode
*change
;
4355 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
4360 modcmd_chanmode_announce(change
);
4361 for(ii
= 0; ii
< change
->argc
; ++ii
)
4362 free((char*)change
->args
[ii
].u
.hostmask
);
4363 mod_chanmode_free(change
);
4368 if(action
& ACTION_DEL_LAMER
)
4370 struct banData
*ban
, *next
;
4372 ban
= channel
->channel_info
->bans
; /* lamers */
4376 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
4379 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
4384 del_channel_ban(ban
);
4391 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
4393 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
4399 static CHANSERV_FUNC(cmd_unban
)
4401 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
4404 static CHANSERV_FUNC(cmd_dellamer
)
4406 /* it doesn't necessarily have to remove the channel ban - may want
4407 to make that an option. */
4408 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
4411 static CHANSERV_FUNC(cmd_unbanme
)
4413 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4414 long flags
= ACTION_UNBAN
;
4416 /* remove permanent bans if the user has the proper access. */
4417 if(uData
->access
>= UL_MANAGER
)
4418 flags
|= ACTION_DEL_LAMER
;
4420 argv
[1] = user
->nick
;
4421 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
4424 static CHANSERV_FUNC(cmd_unbanall
)
4426 struct mod_chanmode
*change
;
4429 if(!channel
->banlist
.used
)
4431 reply("CSMSG_NO_BANS", channel
->name
);
4435 // TODO: dont remove some kinds of extended bans such as ~c
4436 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4437 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4439 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4440 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4442 modcmd_chanmode_announce(change
);
4443 for(ii
= 0; ii
< change
->argc
; ++ii
)
4444 free((char*)change
->args
[ii
].u
.hostmask
);
4445 mod_chanmode_free(change
);
4446 reply("CSMSG_BANS_REMOVED", channel
->name
);
4450 static CHANSERV_FUNC(cmd_open
)
4452 struct mod_chanmode
*change
;
4455 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4457 change
= mod_chanmode_alloc(0);
4458 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4459 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4460 && channel
->channel_info
->modes
.modes_set
)
4461 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4462 modcmd_chanmode_announce(change
);
4463 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4464 for(ii
= 0; ii
< change
->argc
; ++ii
)
4465 free((char*)change
->args
[ii
].u
.hostmask
);
4466 mod_chanmode_free(change
);
4470 static CHANSERV_FUNC(cmd_myaccess
)
4472 static struct string_buffer sbuf
;
4473 struct handle_info
*target_handle
;
4474 struct userData
*uData
;
4477 target_handle
= user
->handle_info
;
4478 else if(!IsStaff(user
))
4480 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4483 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4486 if(!oper_outranks(user
, target_handle
))
4489 if(!target_handle
->channels
)
4491 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4495 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4496 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4498 struct chanData
*cData
= uData
->channel
;
4500 if(uData
->access
> UL_OWNER
)
4502 if(IsProtected(cData
)
4503 && (target_handle
!= user
->handle_info
)
4504 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4507 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4508 if(uData
->flags
!= 0)
4509 string_buffer_append(&sbuf
, ',');
4510 if(IsUserSuspended(uData
))
4511 string_buffer_append(&sbuf
, 's');
4512 if(IsUserAutoOp(uData
))
4514 if(uData
->access
>= UL_OP
)
4515 string_buffer_append(&sbuf
, 'o');
4516 else if(uData
->access
>= UL_HALFOP
)
4517 string_buffer_append(&sbuf
, 'h');
4518 else if(uData
->access
>= UL_PEON
)
4519 string_buffer_append(&sbuf
, 'v');
4521 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4522 string_buffer_append(&sbuf
, 'i');
4523 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4524 string_buffer_append(&sbuf
, 'j');
4526 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4528 string_buffer_append_string(&sbuf
, ")]");
4529 string_buffer_append(&sbuf
, '\0');
4530 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4536 static CHANSERV_FUNC(cmd_access
)
4538 struct userNode
*target
;
4539 struct handle_info
*target_handle
;
4540 struct userData
*uData
;
4542 char prefix
[MAXLEN
];
4547 target_handle
= target
->handle_info
;
4549 else if((target
= GetUserH(argv
[1])))
4551 target_handle
= target
->handle_info
;
4553 else if(argv
[1][0] == '*')
4555 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4557 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4563 reply("MSG_NICK_UNKNOWN", argv
[1]);
4567 assert(target
|| target_handle
);
4569 if(target
== chanserv
)
4571 reply("CSMSG_IS_CHANSERV");
4579 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4584 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4587 reply("MSG_AUTHENTICATE");
4593 const char *epithet
= NULL
, *type
= NULL
;
4596 epithet
= chanserv_conf
.irc_operator_epithet
;
4597 type
= user_find_message(user
, "CSMSG_OPERATOR_TITLE");
4599 else if(IsNetworkHelper(target
))
4601 epithet
= chanserv_conf
.network_helper_epithet
;
4602 type
= user_find_message(user
, "CSMSG_UC_H_TITLE");
4604 else if(IsSupportHelper(target
))
4606 epithet
= chanserv_conf
.support_helper_epithet
;
4607 type
= user_find_message(user
, "CSMSG_LC_H_TITLE");
4611 if(target_handle
->epithet
)
4612 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4614 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4616 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4620 sprintf(prefix
, "%s", target_handle
->handle
);
4623 if(!channel
->channel_info
)
4625 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4629 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4630 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4631 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4633 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4634 /* To prevent possible information leaks, only show infolines
4635 * if the requestor is in the channel or it's their own
4637 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4639 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4641 /* Likewise, only say it's suspended if the user has active
4642 * access in that channel or it's their own entry. */
4643 if(IsUserSuspended(uData
)
4644 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4645 || (user
->handle_info
== uData
->handle
)))
4647 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4652 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4658 /* This is never used...
4660 zoot_list(struct listData *list)
4662 struct userData *uData;
4663 unsigned int start, curr, highest, lowest;
4664 struct helpfile_table tmp_table;
4665 const char **temp, *msg;
4667 if(list->table.length == 1)
4670 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);
4672 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));
4673 msg = user_find_message(list->user, "MSG_NONE");
4674 send_message_type(4, list->user, list->bot, " %s", msg);
4676 tmp_table.width = list->table.width;
4677 tmp_table.flags = list->table.flags;
4678 list->table.contents[0][0] = " ";
4679 highest = list->highest;
4680 if(list->lowest != 0)
4681 lowest = list->lowest;
4682 else if(highest < 100)
4685 lowest = highest - 100;
4686 for(start = curr = 1; curr < list->table.length; )
4688 uData = list->users[curr-1];
4689 list->table.contents[curr++][0] = " ";
4690 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4693 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);
4695 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));
4696 temp = list->table.contents[--start];
4697 list->table.contents[start] = list->table.contents[0];
4698 tmp_table.contents = list->table.contents + start;
4699 tmp_table.length = curr - start;
4700 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4701 list->table.contents[start] = temp;
4703 highest = lowest - 1;
4704 lowest = (highest < 100) ? 0 : (highest - 99);
4711 normal_list(struct listData
*list
)
4715 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
);
4717 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
));
4718 if(list
->table
.length
== 1)
4720 msg
= user_find_message(list
->user
, "MSG_NONE");
4721 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4724 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4727 /* if these need changed, uncomment and customize
4729 clean_list(struct listData *list)
4733 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);
4735 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));
4736 if(list->table.length == 1)
4738 msg = user_find_message(list->user, "MSG_NONE");
4739 send_message_type(4, list->user, list->bot, " %s", msg);
4742 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4746 advanced_list(struct listData *list)
4750 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);
4752 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));
4753 if(list->table.length == 1)
4755 msg = user_find_message(list->user, "MSG_NONE");
4756 send_message_type(4, list->user, list->bot, " %s", msg);
4759 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4763 classic_list(struct listData *list)
4767 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4769 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4770 if(list->table.length == 1)
4772 msg = user_find_message(list->user, "MSG_NONE");
4773 send_message_type(4, list->user, list->bot, " %s", msg);
4776 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4781 userData_access_comp(const void *arg_a
, const void *arg_b
)
4783 const struct userData
*a
= *(struct userData
**)arg_a
;
4784 const struct userData
*b
= *(struct userData
**)arg_b
;
4786 if(a
->access
!= b
->access
)
4787 res
= b
->access
- a
->access
;
4789 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4794 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4796 void (*send_list
)(struct listData
*);
4797 struct userData
*uData
;
4798 struct listData lData
;
4799 unsigned int matches
;
4805 lData
.bot
= cmd
->parent
->bot
;
4806 lData
.channel
= channel
;
4807 lData
.lowest
= lowest
;
4808 lData
.highest
= highest
;
4809 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4810 send_list
= normal_list
;
4811 /* What does the following line do exactly?? */
4812 /*(void)zoot_list; ** since it doesn't show user levels */
4815 if(user->handle_info)
4817 switch(user->handle_info->userlist_style)
4819 case HI_STYLE_CLEAN:
4820 send_list = clean_list;
4822 case HI_STYLE_ADVANCED:
4823 send_list = advanced_list;
4825 case HI_STYLE_CLASSIC:
4826 send_list = classic_list;
4828 case HI_STYLE_NORMAL:
4830 send_list = normal_list;
4835 send_list
= normal_list
;
4837 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4839 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4841 if((uData
->access
< lowest
)
4842 || (uData
->access
> highest
)
4843 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4845 lData
.users
[matches
++] = uData
;
4847 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4849 lData
.table
.length
= matches
+1;
4850 lData
.table
.flags
= TABLE_NO_FREE
;
4851 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4853 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4854 lData
.table
.width
= 6; /* with level = 6 */
4856 lData
.table
.width
= 5; /* without = 5 */
4857 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4858 lData
.table
.contents
[0] = ary
;
4859 if(user
->handle_info
) {
4860 switch(user
->handle_info
->userlist_style
) {
4861 case HI_STYLE_CLASSIC
:
4864 case HI_STYLE_ADVANCED
:
4865 ary
[i
++] = "Access";
4868 case HI_STYLE_CLEAN
:
4869 ary
[i
++] = "Access";
4871 case HI_STYLE_NORMAL
:
4873 ary
[i
++] = "Access";
4878 ary
[i
++] = "Access";
4880 ary
[i
++] = "Account";
4881 ary
[i
] = "Last Seen";
4883 ary
[i
++] = "Status";
4884 ary
[i
++] = "Expiry";
4885 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4887 char seen
[INTERVALLEN
];
4890 uData
= lData
.users
[matches
-1];
4892 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4893 lData
.table
.contents
[matches
] = ary
;
4894 if(user
->handle_info
) {
4895 switch(user
->handle_info
->userlist_style
) {
4896 case HI_STYLE_CLASSIC
:
4897 ary
[i
++] = strtab(uData
->access
);
4899 case HI_STYLE_ADVANCED
:
4900 ary
[i
++] = user_level_name_from_level(uData
->access
);
4901 ary
[i
++] = strtab(uData
->access
);
4903 case HI_STYLE_CLEAN
:
4904 ary
[i
++] = user_level_name_from_level(uData
->access
);
4906 case HI_STYLE_NORMAL
:
4908 ary
[i
++] = user_level_name_from_level(uData
->access
);
4913 ary
[i
++] = user_level_name_from_level(uData
->access
);
4915 ary
[i
++] = uData
->handle
->handle
;
4918 else if(!uData
->seen
)
4921 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4922 ary
[i
] = strdup(ary
[i
]);
4924 if(IsUserSuspended(uData
))
4925 ary
[i
++] = "Suspended";
4926 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4927 ary
[i
++] = "Vacation";
4929 ary
[i
++] = "Normal";
4931 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4932 char delay
[INTERVALLEN
];
4935 if (uData
->accessexpiry
> 0) {
4936 diff
= uData
->accessexpiry
- now
;
4937 intervalString(delay
, diff
, user
->handle_info
);
4939 diff
= uData
->clvlexpiry
- now
;
4940 intervalString(delay
, diff
, user
->handle_info
);
4948 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4950 /* Free strdup above */
4951 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4952 free(lData
.table
.contents
[matches
]);
4954 free(lData
.table
.contents
[0]);
4955 free(lData
.table
.contents
);
4959 /* Remove this now that debugging is over? or improve it for
4960 * users? Would it be better tied into USERS somehow? -Rubin */
4961 static CHANSERV_FUNC(cmd_pending
)
4963 struct adduserPending
*ap
;
4964 reply("CSMSG_ADDUSER_PENDING_HEADER");
4965 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4967 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4968 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4969 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4973 static CHANSERV_FUNC(cmd_users
)
4975 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4978 static CHANSERV_FUNC(cmd_wlist
)
4980 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4983 static CHANSERV_FUNC(cmd_clist
)
4985 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4988 static CHANSERV_FUNC(cmd_mlist
)
4990 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4993 static CHANSERV_FUNC(cmd_olist
)
4995 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4998 static CHANSERV_FUNC(cmd_hlist
)
5000 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
5003 static CHANSERV_FUNC(cmd_plist
)
5005 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
5008 static CHANSERV_FUNC(cmd_lamers
)
5010 struct userNode
*search_u
= NULL
;
5011 struct helpfile_table tbl
;
5012 unsigned int matches
= 0, timed
= 0, search_wilds
= 0, ii
;
5013 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
5014 const char *msg_never
, *triggered
, *expires
;
5015 struct banData
*ban
, **bans
; /* lamers */
5019 else if(strchr(search
= argv
[1], '!'))
5022 search_wilds
= search
[strcspn(search
, "?*")];
5024 else if(!(search_u
= GetUserH(search
)))
5025 reply("MSG_NICK_UNKNOWN", search
);
5027 reply("CSMSG_LAMERS_HEADER", channel
->name
);
5028 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
5031 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
5035 if(!user_matches_glob(search_u
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
5040 if(search_wilds
? !match_ircglobs(search
, ban
->mask
) : !match_ircglob(search
, ban
->mask
))
5043 bans
[matches
++] = ban
;
5048 tbl
.length
= matches
+ 1;
5049 tbl
.width
= 4 + timed
;
5051 tbl
.flags
= TABLE_NO_FREE
;
5052 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
5053 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5054 tbl
.contents
[0][0] = "Mask";
5055 tbl
.contents
[0][1] = "Set By";
5056 tbl
.contents
[0][2] = "Triggered";
5059 tbl
.contents
[0][3] = "Expires";
5060 tbl
.contents
[0][4] = "Reason";
5063 tbl
.contents
[0][3] = "Reason";
5066 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5067 /* reply("MSG_NONE"); */
5068 free(tbl
.contents
[0]);
5073 msg_never
= user_find_message(user
, "MSG_NEVER");
5074 for(ii
= 0; ii
< matches
; )
5080 else if(ban
->expires
)
5081 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
5083 expires
= msg_never
;
5086 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
5088 triggered
= msg_never
;
5090 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5091 tbl
.contents
[ii
][0] = ban
->mask
;
5092 tbl
.contents
[ii
][1] = ban
->owner
;
5093 tbl
.contents
[ii
][2] = strdup(triggered
);
5096 tbl
.contents
[ii
][3] = strdup(expires
);
5097 tbl
.contents
[ii
][4] = ban
->reason
;
5100 tbl
.contents
[ii
][3] = ban
->reason
;
5102 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5103 /* reply("MSG_MATCH_COUNT", matches); */
5104 for(ii
= 1; ii
< tbl
.length
; ++ii
)
5106 free((char*)tbl
.contents
[ii
][2]);
5108 free((char*)tbl
.contents
[ii
][3]);
5109 free(tbl
.contents
[ii
]);
5111 free(tbl
.contents
[0]);
5118 * return + if the user does NOT have the right to set the topic, and
5119 * the topic is changed.
5122 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
5124 struct chanData
*cData
= channel
->channel_info
;
5125 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5127 else if(cData
->topic
)
5128 return irccasecmp(new_topic
, cData
->topic
);
5135 * Makes a givin topic fit into a givin topic mask and returns
5138 * topic_mask - the mask to conform to
5139 * topic - the topic to make conform
5140 * new_topic - the pre-allocated char* to put the new topic into
5142 * modifies: new_topic
5145 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
5147 //char *topic_mask = cData->topic_mask;
5149 int pos
=0, starpos
=-1, dpos
=0, len
;
5151 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
5158 strcpy(new_topic
, "");
5161 len
= strlen(topic
);
5162 if((dpos
+ len
) > TOPICLEN
)
5163 len
= TOPICLEN
+ 1 - dpos
;
5164 memcpy(new_topic
+dpos
, topic
, len
);
5168 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
5169 default: new_topic
[dpos
++] = tchar
; break;
5172 if((dpos
> TOPICLEN
) || tchar
)
5174 strcpy(new_topic
, "");
5177 new_topic
[dpos
] = 0;
5181 static CHANSERV_FUNC(cmd_topic
)
5183 struct chanData
*cData
;
5187 #ifdef WITH_PROTOCOL_P10
5191 cData
= channel
->channel_info
;
5196 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5197 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
5198 reply("CSMSG_TOPIC_SET", cData
->topic
);
5202 reply("CSMSG_NO_TOPIC", channel
->name
);
5206 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5207 /* If they say "!topic *", use an empty topic. */
5208 if((topic
[0] == '*') && (topic
[1] == 0))
5211 if(bad_topic(channel
, user
, topic
))
5213 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5218 /* If there is a topicmask set, and the new topic doesnt match, make it */
5219 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
5221 char *topic_mask
= cData
->topic_mask
;
5222 char new_topic
[TOPICLEN
+1];
5224 /* make a new topic fitting mask */
5225 conform_topic(topic_mask
, topic
, new_topic
);
5228 /* Topic couldnt fit into mask, was too long */
5229 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
5230 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
5233 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
5235 else /* No mask set, just set the topic */
5236 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
5239 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
5241 /* Grab the topic and save it as the default topic. */
5243 cData
->topic
= strdup(channel
->topic
);
5249 static CHANSERV_FUNC(cmd_mode
)
5251 struct userData
*uData
;
5252 struct mod_chanmode
*change
;
5257 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5258 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5262 change
= &channel
->channel_info
->modes
;
5263 if(change
->modes_set
|| change
->modes_clear
) {
5264 modcmd_chanmode_announce(change
);
5265 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
5267 reply("CSMSG_NO_MODES", channel
->name
);
5271 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5273 base_oplevel
= MAXOPLEVEL
;
5274 else if (uData
->access
>= UL_OWNER
)
5277 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
5278 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
5282 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
5286 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
5287 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
5290 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5291 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
5295 modcmd_chanmode_announce(change
);
5296 mod_chanmode_free(change
);
5297 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
5301 static CHANSERV_FUNC(cmd_invite
)
5303 struct userData
*uData
;
5304 struct userNode
*invite
;
5306 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5310 if(!(invite
= GetUserH(argv
[1])))
5312 reply("MSG_NICK_UNKNOWN", argv
[1]);
5319 if(GetUserMode(channel
, invite
))
5321 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
5329 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5330 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
5333 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
5336 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
5338 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
5339 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
, 0)) {
5340 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
5346 irc_invite(chanserv
, invite
, channel
);
5348 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
5353 static CHANSERV_FUNC(cmd_inviteme
)
5355 if(GetUserMode(channel
, user
))
5357 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
5360 if(channel
->channel_info
5361 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
5363 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
5366 irc_invite(cmd
->parent
->bot
, user
, channel
);
5371 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
5374 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
5376 /* We display things based on two dimensions:
5377 * - Issue time: present or absent
5378 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5379 * (in order of precedence, so something both expired and revoked
5380 * only counts as revoked)
5382 combo
= (suspended
->issued
? 4 : 0)
5383 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
5385 case 0: /* no issue time, indefinite expiration */
5386 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
5388 case 1: /* no issue time, expires in future */
5389 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
5390 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
5392 case 2: /* no issue time, expired */
5393 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
5394 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
5396 case 3: /* no issue time, revoked */
5397 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
5398 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
5400 case 4: /* issue time set, indefinite expiration */
5401 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5402 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
5404 case 5: /* issue time set, expires in future */
5405 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5406 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
5407 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5409 case 6: /* issue time set, expired */
5410 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5411 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
5412 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5414 case 7: /* issue time set, revoked */
5415 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5416 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
5417 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5420 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
5426 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
5429 const char *fmt
= "%a %b %d %H:%M %Y";
5430 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
5432 if(giveownership
->staff_issuer
)
5434 if(giveownership
->reason
)
5435 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
5436 giveownership
->target
, giveownership
->target_access
,
5437 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
5439 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
5440 giveownership
->target
, giveownership
->target_access
,
5441 giveownership
->staff_issuer
, buf
);
5445 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5450 static CHANSERV_FUNC(cmd_info
)
5452 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5453 struct userData
*uData
, *owner
;
5454 struct chanData
*cData
;
5455 struct do_not_register
*dnr
;
5460 cData
= channel
->channel_info
;
5461 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5462 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5465 uData
= GetChannelUser(cData
, user
->handle_info
);
5466 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5468 mod_chanmode_format(&cData
->modes
, modes
);
5469 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5470 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5473 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5477 note
= iter_data(it
);
5478 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5481 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5482 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5485 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5486 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5487 if(owner
->access
== UL_OWNER
)
5488 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5489 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5490 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5491 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5493 privileged
= IsStaff(user
);
5494 /* if(privileged) */
5495 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5496 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5497 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5499 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5500 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5502 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5504 struct suspended
*suspended
;
5505 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5506 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5507 show_suspension_info(cmd
, user
, suspended
);
5509 else if(IsSuspended(cData
))
5511 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5512 show_suspension_info(cmd
, user
, cData
->suspended
);
5514 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5516 struct giveownership
*giveownership
;
5517 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5518 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5519 show_giveownership_info(cmd
, user
, giveownership
);
5521 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5522 reply("CSMSG_CHANNEL_END");
5524 reply("CSMSG_CHANNEL_END_CLEAN");
5528 static CHANSERV_FUNC(cmd_netinfo
)
5530 extern time_t boot_time
;
5531 extern unsigned long burst_length
;
5532 char interval
[INTERVALLEN
];
5534 reply("CSMSG_NETWORK_INFO");
5535 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5536 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5537 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5538 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5539 reply("CSMSG_NETWORK_LAMERS", banCount
);
5540 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5541 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5542 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5547 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5549 struct helpfile_table table
;
5551 struct userNode
*user
;
5556 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5557 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5558 for(nn
=0; nn
<list
->used
; nn
++)
5560 user
= list
->list
[nn
];
5561 if(user
->modes
& skip_flags
)
5563 if(IsBot(user
) || IsHideOper(user
))
5565 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5568 nick
= alloca(strlen(user
->nick
)+3);
5569 sprintf(nick
, "(%s)", user
->nick
);
5573 table
.contents
[table
.length
][0] = nick
;
5576 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5579 static CHANSERV_FUNC(cmd_ircops
)
5581 reply("CSMSG_STAFF_OPERS");
5582 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5586 static CHANSERV_FUNC(cmd_helpers
)
5588 reply("CSMSG_STAFF_HELPERS");
5589 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5593 static CHANSERV_FUNC(cmd_staff
)
5595 reply("CSMSG_NETWORK_STAFF");
5596 cmd_ircops(CSFUNC_ARGS
);
5597 cmd_helpers(CSFUNC_ARGS
);
5601 static CHANSERV_FUNC(cmd_peek
)
5603 struct modeNode
*mn
;
5604 char modes
[MODELEN
];
5606 struct helpfile_table table
;
5608 irc_make_chanmode(channel
, modes
);
5610 reply("CSMSG_PEEK_INFO", channel
->name
);
5611 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5613 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5614 reply("CSMSG_PEEK_MODES", modes
);
5615 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5619 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5620 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5621 for(n
= 0; n
< channel
->members
.used
; n
++)
5623 mn
= channel
->members
.list
[n
];
5624 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5626 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5627 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5632 reply("CSMSG_PEEK_OPS");
5633 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5636 reply("CSMSG_PEEK_NO_OPS");
5637 reply("CSMSG_PEEK_END");
5641 static MODCMD_FUNC(cmd_wipeinfo
)
5643 struct handle_info
*victim
;
5644 struct userData
*ud
, *actor
, *real_actor
;
5645 unsigned int override
= 0;
5648 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5649 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5650 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5652 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5654 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5657 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5659 reply("MSG_USER_OUTRANKED", victim
->handle
);
5662 if((ud
!= real_actor
) && (!real_actor
|| (ud
->access
>= real_actor
->access
)))
5663 override
= CMD_LOG_OVERRIDE
;
5667 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5668 return 1 | override
;
5672 resync_channel(struct chanNode
*channel
)
5674 struct mod_chanmode
*changes
;
5675 struct chanData
*cData
= channel
->channel_info
;
5676 unsigned int ii
, used
;
5678 /* 6 = worst case -ovh+ovh on everyone */
5679 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5680 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5682 struct modeNode
*mn
= channel
->members
.list
[ii
];
5683 struct userData
*uData
;
5685 if(IsService(mn
->user
))
5689 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5691 /* If the channel is in no-mode mode, de-mode EVERYONE */
5692 if(cData
->chOpts
[chAutomode
] == 'n')
5696 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5697 changes
->args
[used
++].u
.member
= mn
;
5700 else /* Give various userlevels their modes.. */
5702 /* If the user has autoop/autovoice disabled then ignore them */
5703 if(uData
&& !IsUserAutoOp(uData
))
5705 if(uData
&& uData
->access
>= UL_OP
)
5707 if(!(mn
->modes
& MODE_CHANOP
))
5709 changes
->args
[used
].mode
= MODE_CHANOP
;
5710 changes
->args
[used
++].u
.member
= mn
;
5713 else if(uData
&& uData
->access
>= UL_HALFOP
)
5715 if(mn
->modes
& MODE_CHANOP
)
5717 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5718 changes
->args
[used
++].u
.member
= mn
;
5720 if(!(mn
->modes
& MODE_HALFOP
))
5722 changes
->args
[used
].mode
= MODE_HALFOP
;
5723 changes
->args
[used
++].u
.member
= mn
;
5726 else if(uData
&& uData
->access
>= UL_PEON
)
5728 if(mn
->modes
& MODE_CHANOP
)
5730 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5731 changes
->args
[used
++].u
.member
= mn
;
5733 if(mn
->modes
& MODE_HALFOP
)
5735 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5736 changes
->args
[used
++].u
.member
= mn
;
5738 /* Don't voice peons if were in mode m */
5739 if( cData
->chOpts
[chAutomode
] == 'm')
5741 if(mn
->modes
& MODE_VOICE
)
5743 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5744 changes
->args
[used
++].u
.member
= mn
;
5747 /* otherwise, make user they do have voice */
5748 else if(!(mn
->modes
& MODE_VOICE
))
5750 changes
->args
[used
].mode
= MODE_VOICE
;
5751 changes
->args
[used
++].u
.member
= mn
;
5754 else /* They arnt on the userlist.. */
5756 /* If we voice everyone, but they dont.. */
5757 if(cData
->chOpts
[chAutomode
] == 'v')
5759 /* Remove anything except v */
5760 if(mn
->modes
& ~MODE_VOICE
)
5762 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5763 changes
->args
[used
++].u
.member
= mn
;
5766 if(!(mn
->modes
& MODE_VOICE
))
5768 changes
->args
[used
].mode
= MODE_VOICE
;
5769 changes
->args
[used
++].u
.member
= mn
;
5772 /* If we hop everyone, but they dont.. */
5773 else if(cData
->chOpts
[chAutomode
] == 'h')
5775 /* Remove anything except h */
5776 if(mn
->modes
& ~MODE_HALFOP
)
5778 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5779 changes
->args
[used
++].u
.member
= mn
;
5782 if(!(mn
->modes
& MODE_HALFOP
))
5784 changes
->args
[used
].mode
= MODE_HALFOP
;
5785 changes
->args
[used
++].u
.member
= mn
;
5788 /* If we op everyone, but they dont.. */
5789 else if(cData
->chOpts
[chAutomode
] == 'o')
5791 /* Remove anything except h */
5792 if(mn
->modes
& ~MODE_CHANOP
)
5794 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5795 changes
->args
[used
++].u
.member
= mn
;
5798 if(!(mn
->modes
& MODE_CHANOP
))
5800 changes
->args
[used
].mode
= MODE_CHANOP
;
5801 changes
->args
[used
++].u
.member
= mn
;
5804 /* they have no excuse for having modes, de-everything them */
5809 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5810 changes
->args
[used
++].u
.member
= mn
;
5816 changes
->argc
= used
;
5817 mod_chanmode_announce(chanserv
, channel
, changes
);
5818 mod_chanmode_free(changes
);
5821 static CHANSERV_FUNC(cmd_resync
)
5823 resync_channel(channel
);
5824 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5828 static CHANSERV_FUNC(cmd_seen
)
5830 struct userData
*uData
;
5831 struct handle_info
*handle
;
5832 char seen
[INTERVALLEN
];
5836 if(!irccasecmp(argv
[1], chanserv
->nick
))
5838 reply("CSMSG_IS_CHANSERV");
5842 if(!(handle
= get_handle_info(argv
[1])))
5844 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5848 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5850 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5855 reply("CSMSG_USER_PRESENT", handle
->handle
);
5856 else if(uData
->seen
)
5857 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5859 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5861 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5862 reply("CSMSG_USER_VACATION", handle
->handle
);
5867 static MODCMD_FUNC(cmd_names
)
5869 struct userNode
*targ
;
5870 struct userData
*targData
;
5871 unsigned int ii
, pos
;
5874 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5876 targ
= channel
->members
.list
[ii
]->user
;
5877 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5880 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5883 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5887 if(IsUserSuspended(targData
))
5889 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5892 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5893 reply("CSMSG_END_NAMES", channel
->name
);
5898 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5900 switch(ntype
->visible_type
)
5902 case NOTE_VIS_ALL
: return 1;
5903 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5904 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5909 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5911 struct userData
*uData
;
5913 switch(ntype
->set_access_type
)
5915 case NOTE_SET_CHANNEL_ACCESS
:
5916 if(!user
->handle_info
)
5918 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5920 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5921 case NOTE_SET_CHANNEL_SETTER
:
5922 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5923 case NOTE_SET_PRIVILEGED
: default:
5924 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5928 static CHANSERV_FUNC(cmd_note
)
5930 struct chanData
*cData
;
5932 struct note_type
*ntype
;
5934 cData
= channel
->channel_info
;
5937 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5941 /* If no arguments, show all visible notes for the channel. */
5947 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5949 note
= iter_data(it
);
5950 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5953 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5954 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5957 reply("CSMSG_NOTELIST_END", channel
->name
);
5959 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5961 /* If one argument, show the named note. */
5964 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5965 && note_type_visible_to_user(cData
, note
->type
, user
))
5967 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5969 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5970 && note_type_visible_to_user(NULL
, ntype
, user
))
5972 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5977 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5981 /* Assume they're trying to set a note. */
5985 ntype
= dict_find(note_types
, argv
[1], NULL
);
5988 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5991 else if(note_type_settable_by_user(channel
, ntype
, user
))
5993 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5994 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5995 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5996 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5997 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5999 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
6001 /* The note is viewable to staff only, so return 0
6002 to keep the invocation from getting logged (or
6003 regular users can see it in !events). */
6009 reply("CSMSG_NO_ACCESS");
6016 static CHANSERV_FUNC(cmd_delnote
)
6021 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
6022 || !note_type_settable_by_user(channel
, note
->type
, user
))
6024 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
6027 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
6028 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
6032 static CHANSERV_FUNC(cmd_last
)
6038 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
6040 if(numoflines
< 1 || numoflines
> 200)
6042 reply("CSMSG_LAST_INVALID");
6045 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
6049 static CHANSERV_FUNC(cmd_events
)
6051 struct logSearch discrim
;
6052 struct logReport report
;
6053 unsigned int matches
, limit
;
6055 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
6056 if(limit
< 1 || limit
> 200)
6059 memset(&discrim
, 0, sizeof(discrim
));
6060 discrim
.masks
.bot
= chanserv
;
6061 discrim
.masks
.channel_name
= channel
->name
;
6063 discrim
.masks
.command
= argv
[2];
6064 discrim
.limit
= limit
;
6065 discrim
.max_time
= INT_MAX
;
6066 discrim
.severities
= 1 << LOG_COMMAND
;
6067 report
.reporter
= chanserv
;
6069 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
6070 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6072 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
6074 reply("MSG_MATCH_COUNT", matches
);
6076 reply("MSG_NO_MATCHES");
6080 static CHANSERV_FUNC(cmd_say
)
6086 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6087 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
6089 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6091 struct handle_info
*hi
;
6092 struct userNode
*authed
;
6095 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6097 if (!(hi
= get_handle_info(argv
[1] + 1)))
6099 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6103 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6104 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "%s", msg
);
6106 else if(GetUserH(argv
[1]))
6109 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6110 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
6114 reply("MSG_NOT_TARGET_NAME");
6120 static CHANSERV_FUNC(cmd_emote
)
6126 /* CTCP is so annoying. */
6127 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6128 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6130 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6132 struct handle_info
*hi
;
6133 struct userNode
*authed
;
6136 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6138 if (!(hi
= get_handle_info(argv
[1] + 1)))
6140 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6144 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6145 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6147 else if(GetUserH(argv
[1]))
6149 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6150 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6154 reply("MSG_NOT_TARGET_NAME");
6160 struct channelList
*
6161 chanserv_support_channels(void)
6163 return &chanserv_conf
.support_channels
;
6166 static CHANSERV_FUNC(cmd_expire
)
6168 int channel_count
= registered_channels
;
6169 expire_channels(NULL
);
6170 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
6175 chanserv_expire_suspension(void *data
)
6177 struct suspended
*suspended
= data
;
6178 struct chanNode
*channel
;
6181 /* Update the channel registration data structure. */
6182 if(!suspended
->expires
|| (now
< suspended
->expires
))
6183 suspended
->revoked
= now
;
6184 channel
= suspended
->cData
->channel
;
6185 suspended
->cData
->channel
= channel
;
6186 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
6188 /* If appropriate, re-join ChanServ to the channel. */
6189 if(!IsOffChannel(suspended
->cData
))
6191 spamserv_cs_suspend(channel
, 0, 0, NULL
);
6192 ss_cs_join_channel(channel
, 1);
6195 /* Mark everyone currently in the channel as present. */
6196 for(ii
= 0; ii
< channel
->members
.used
; ++ii
)
6198 struct userData
*uData
= GetChannelAccess(suspended
->cData
, channel
->members
.list
[ii
]->user
->handle_info
);
6207 static CHANSERV_FUNC(cmd_csuspend
)
6209 struct suspended
*suspended
;
6210 char reason
[MAXLEN
];
6211 time_t expiry
, duration
;
6212 struct userData
*uData
;
6216 if(IsProtected(channel
->channel_info
))
6218 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
6222 if(argv
[1][0] == '!')
6224 else if(IsSuspended(channel
->channel_info
))
6226 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
6227 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
6231 if(!strcmp(argv
[1], "0"))
6233 else if((duration
= ParseInterval(argv
[1])))
6234 expiry
= now
+ duration
;
6237 reply("MSG_INVALID_DURATION", argv
[1]);
6241 unsplit_string(argv
+ 2, argc
- 2, reason
);
6243 suspended
= calloc(1, sizeof(*suspended
));
6244 suspended
->revoked
= 0;
6245 suspended
->issued
= now
;
6246 suspended
->suspender
= strdup(user
->handle_info
->handle
);
6247 suspended
->expires
= expiry
;
6248 suspended
->reason
= strdup(reason
);
6249 suspended
->cData
= channel
->channel_info
;
6250 suspended
->previous
= suspended
->cData
->suspended
;
6251 suspended
->cData
->suspended
= suspended
;
6253 if(suspended
->expires
)
6254 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6256 if(IsSuspended(channel
->channel_info
))
6258 suspended
->previous
->revoked
= now
;
6259 if(suspended
->previous
->expires
)
6260 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
6262 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
6263 channel
->name
, suspended
->suspender
);
6267 /* Mark all users in channel as absent. */
6268 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
6277 /* Mark the channel as suspended, then part. */
6278 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
6279 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
6280 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
6281 reply("CSMSG_SUSPENDED", channel
->name
);
6282 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
6283 channel
->name
, suspended
->suspender
);
6288 static CHANSERV_FUNC(cmd_cunsuspend
)
6290 struct suspended
*suspended
;
6292 if(!IsSuspended(channel
->channel_info
))
6294 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
6298 suspended
= channel
->channel_info
->suspended
;
6300 /* Expire the suspension and join ChanServ to the channel. */
6301 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
6302 chanserv_expire_suspension(suspended
);
6303 reply("CSMSG_UNSUSPENDED", channel
->name
);
6304 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
6305 channel
->name
, user
->handle_info
->handle
);
6309 typedef struct chanservSearch
6317 unsigned long flags
;
6321 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
6324 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
6329 search
= malloc(sizeof(struct chanservSearch
));
6330 memset(search
, 0, sizeof(*search
));
6333 for(i
= 0; i
< argc
; i
++)
6335 /* Assume all criteria require arguments. */
6338 reply("MSG_MISSING_PARAMS", argv
[i
]);
6342 if(!irccasecmp(argv
[i
], "name"))
6343 search
->name
= argv
[++i
];
6344 else if(!irccasecmp(argv
[i
], "registrar"))
6345 search
->registrar
= argv
[++i
];
6346 else if(!irccasecmp(argv
[i
], "unvisited"))
6347 search
->unvisited
= ParseInterval(argv
[++i
]);
6348 else if(!irccasecmp(argv
[i
], "registered"))
6349 search
->registered
= ParseInterval(argv
[++i
]);
6350 else if(!irccasecmp(argv
[i
], "flags"))
6353 if(!irccasecmp(argv
[i
], "nodelete"))
6354 search
->flags
|= CHANNEL_NODELETE
;
6355 else if(!irccasecmp(argv
[i
], "suspended"))
6356 search
->flags
|= CHANNEL_SUSPENDED
;
6357 else if(!irccasecmp(argv
[i
], "unreviewed"))
6358 search
->flags
|= CHANNEL_UNREVIEWED
;
6361 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
6365 else if(!irccasecmp(argv
[i
], "limit"))
6366 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
6369 reply("MSG_INVALID_CRITERIA", argv
[i
]);
6374 if(search
->name
&& !strcmp(search
->name
, "*"))
6376 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
6377 search
->registrar
= 0;
6386 chanserv_channel_match(struct chanData
*channel
, search_t search
)
6388 const char *name
= channel
->channel
->name
;
6389 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
6390 (search
->registrar
&& !channel
->registrar
) ||
6391 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
6392 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
6393 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
6394 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
6401 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
6403 struct chanData
*channel
;
6404 unsigned int matches
= 0;
6406 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
6408 if(!chanserv_channel_match(channel
, search
))
6418 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
6423 search_print(struct chanData
*channel
, void *data
)
6425 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
6428 static CHANSERV_FUNC(cmd_search
)
6431 unsigned int matches
;
6432 channel_search_func action
;
6436 if(!irccasecmp(argv
[1], "count"))
6437 action
= search_count
;
6438 else if(!irccasecmp(argv
[1], "print"))
6439 action
= search_print
;
6442 reply("CSMSG_ACTION_INVALID", argv
[1]);
6446 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
6450 if(action
== search_count
)
6451 search
->limit
= INT_MAX
;
6453 if(action
== search_print
)
6455 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6456 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6460 matches
= chanserv_channel_search(search
, action
, user
);
6463 reply("MSG_MATCH_COUNT", matches
);
6465 reply("MSG_NO_MATCHES");
6471 static CHANSERV_FUNC(cmd_unvisited
)
6473 struct chanData
*cData
;
6474 time_t interval
= chanserv_conf
.channel_expire_delay
;
6475 char buffer
[INTERVALLEN
];
6476 unsigned int limit
= 25, matches
= 0;
6480 interval
= ParseInterval(argv
[1]);
6482 limit
= atoi(argv
[2]);
6485 intervalString(buffer
, interval
, user
->handle_info
);
6486 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
6488 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
6490 if((now
- cData
->visited
) < interval
)
6493 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
6494 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
6501 static MODCMD_FUNC(chan_opt_unreviewed
)
6503 struct chanData
*cData
= channel
->channel_info
;
6504 int value
= (cData
->flags
& CHANNEL_UNREVIEWED
) ? 1 : 0;
6510 /* The two directions can have different ACLs. */
6511 if(enabled_string(argv
[1]))
6513 else if(disabled_string(argv
[1]))
6517 reply("MSG_INVALID_BINARY", argv
[1]);
6521 if (new_value
!= value
)
6523 struct svccmd
*subcmd
;
6524 char subcmd_name
[32];
6526 snprintf(subcmd_name
, sizeof(subcmd_name
), "%s %s", argv
[0], (new_value
? "on" : "off"));
6527 subcmd
= dict_find(cmd
->parent
->commands
, subcmd_name
, NULL
);
6530 reply("MSG_COMMAND_DISABLED", subcmd_name
);
6533 else if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
6537 cData
->flags
|= CHANNEL_UNREVIEWED
;
6540 free(cData
->registrar
);
6541 cData
->registrar
= strdup(user
->handle_info
->handle
);
6542 cData
->flags
&= ~CHANNEL_UNREVIEWED
;
6549 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_ON"));
6551 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_OFF"));
6555 static MODCMD_FUNC(chan_opt_defaulttopic
)
6561 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6563 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6567 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6569 free(channel
->channel_info
->topic
);
6570 if(topic
[0] == '*' && topic
[1] == 0)
6572 topic
= channel
->channel_info
->topic
= NULL
;
6576 topic
= channel
->channel_info
->topic
= strdup(topic
);
6577 if(channel
->channel_info
->topic_mask
6578 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6579 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6581 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6584 if(channel
->channel_info
->topic
)
6585 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6587 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6591 static MODCMD_FUNC(chan_opt_topicmask
)
6595 struct chanData
*cData
= channel
->channel_info
;
6598 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6600 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6604 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6606 if(cData
->topic_mask
)
6607 free(cData
->topic_mask
);
6608 if(mask
[0] == '*' && mask
[1] == 0)
6610 cData
->topic_mask
= 0;
6614 cData
->topic_mask
= strdup(mask
);
6616 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6617 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6618 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6622 if(channel
->channel_info
->topic_mask
)
6623 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6625 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6629 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6633 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6637 if(greeting
[0] == '*' && greeting
[1] == 0)
6641 unsigned int length
= strlen(greeting
);
6642 if(length
> chanserv_conf
.greeting_length
)
6644 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6647 *data
= strdup(greeting
);
6656 reply(name
, user_find_message(user
, "MSG_NONE"));
6660 static MODCMD_FUNC(chan_opt_greeting
)
6662 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6665 static MODCMD_FUNC(chan_opt_usergreeting
)
6667 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6670 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6672 unsigned int charmax
;
6675 charmax
= atoi(argv
[1]);
6676 if ((charmax
> 0) && (charmax
<= chanserv_conf
.max_userinfo_length
))
6677 channel
->channel_info
->maxsetinfo
= charmax
;
6680 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6684 static MODCMD_FUNC(chan_opt_modes
)
6686 struct mod_chanmode
*new_modes
;
6687 char modes
[MODELEN
];
6691 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6692 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6696 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6698 reply("CSMSG_NO_ACCESS");
6701 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6703 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6705 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6707 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6710 else if(new_modes
->argc
> 1)
6712 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6713 mod_chanmode_free(new_modes
);
6718 channel
->channel_info
->modes
= *new_modes
;
6719 modcmd_chanmode_announce(new_modes
);
6720 mod_chanmode_free(new_modes
);
6724 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6726 reply("CSMSG_SET_MODES", modes
);
6728 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6732 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6734 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6736 struct chanData
*cData
= channel
->channel_info
;
6741 /* Set flag according to value. */
6742 if(enabled_string(argv
[1]))
6744 cData
->flags
|= mask
;
6747 else if(disabled_string(argv
[1]))
6749 cData
->flags
&= ~mask
;
6754 reply("MSG_INVALID_BINARY", argv
[1]);
6760 /* Find current option value. */
6761 value
= (cData
->flags
& mask
) ? 1 : 0;
6765 reply(name
, user_find_message(user
, "MSG_ON"));
6767 reply(name
, user_find_message(user
, "MSG_OFF"));
6771 static MODCMD_FUNC(chan_opt_nodelete
)
6773 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6775 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6779 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6782 static MODCMD_FUNC(chan_opt_dynlimit
)
6784 struct mod_chanmode change
;
6787 if (disabled_string(argv
[1])) {
6788 mod_chanmode_init(&change
);
6789 change
.modes_clear
|= MODE_LIMIT
;
6790 mod_chanmode_announce(chanserv
, channel
, &change
);
6794 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6797 static MODCMD_FUNC(chan_opt_offchannel
)
6799 struct chanData
*cData
= channel
->channel_info
;
6804 /* Set flag according to value. */
6805 if(enabled_string(argv
[1]))
6807 if(!IsOffChannel(cData
))
6808 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6809 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6812 else if(disabled_string(argv
[1]))
6814 if(IsOffChannel(cData
))
6816 struct mod_chanmode change
;
6817 mod_chanmode_init(&change
);
6819 change
.args
[0].mode
= MODE_CHANOP
;
6820 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6821 mod_chanmode_announce(chanserv
, channel
, &change
);
6823 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6828 reply("MSG_INVALID_BINARY", argv
[1]);
6834 /* Find current option value. */
6835 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6839 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6841 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6845 static MODCMD_FUNC(chan_opt_defaults
)
6847 struct userData
*uData
;
6848 struct chanData
*cData
;
6849 const char *confirm
;
6850 enum levelOption lvlOpt
;
6851 enum charOption chOpt
;
6853 cData
= channel
->channel_info
;
6854 uData
= GetChannelUser(cData
, user
->handle_info
);
6855 if(!uData
|| (uData
->access
< UL_OWNER
))
6857 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6860 confirm
= make_confirmation_string(uData
);
6861 if((argc
< 2) || strcmp(argv
[1], confirm
))
6863 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6866 cData
->flags
= (CHANNEL_DEFAULT_FLAGS
& ~CHANNEL_PRESERVED_FLAGS
)
6867 | (cData
->flags
& CHANNEL_PRESERVED_FLAGS
);
6868 cData
->modes
= chanserv_conf
.default_modes
;
6869 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6870 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6871 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6872 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6873 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6878 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6880 struct chanData
*cData
= channel
->channel_info
;
6881 struct userData
*uData
;
6882 unsigned short value
;
6886 if(!check_user_level(channel
, user
, option
, 1, 1))
6888 reply("CSMSG_CANNOT_SET");
6891 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6892 if(!value
&& strcmp(argv
[1], "0"))
6894 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6897 uData
= GetChannelUser(cData
, user
->handle_info
);
6898 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6900 reply("CSMSG_BAD_SETLEVEL");
6906 /* This test only applies to owners, since non-owners
6907 * trying to set an option to above their level get caught
6908 * by the CSMSG_BAD_SETLEVEL test above.
6910 if(value
> uData
->access
)
6912 reply("CSMSG_BAD_SETTERS");
6919 cData
->lvlOpts
[option
] = value
;
6921 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6925 static MODCMD_FUNC(chan_opt_enfops
)
6927 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6930 static MODCMD_FUNC(chan_opt_enfhalfops
)
6932 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6934 static MODCMD_FUNC(chan_opt_enfmodes
)
6936 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6939 static MODCMD_FUNC(chan_opt_enftopic
)
6941 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6944 static MODCMD_FUNC(chan_opt_pubcmd
)
6946 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6949 static MODCMD_FUNC(chan_opt_setters
)
6951 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6954 static MODCMD_FUNC(chan_opt_userinfo
)
6956 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6959 static MODCMD_FUNC(chan_opt_topicsnarf
)
6961 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6964 static MODCMD_FUNC(chan_opt_inviteme
)
6966 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6969 /* TODO: Make look like this when no args are
6971 * -X3- -------------------------------
6972 * -X3- BanTimeout: Bans are removed:
6973 * -X3- ----- * indicates current -----
6974 * -X3- 0: [*] Never.
6975 * -X3- 1: [ ] After 10 minutes.
6976 * -X3- 2: [ ] After 2 hours.
6977 * -X3- 3: [ ] After 4 hours.
6978 * -X3- 4: [ ] After 24 hours.
6979 * -X3- 5: [ ] After one week.
6980 * -X3- ------------- End -------------
6983 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6985 struct chanData
*cData
= channel
->channel_info
;
6986 int count
= charOptions
[option
].count
, idx
;
6990 idx
= atoi(argv
[1]);
6992 if(!isdigit(argv
[1][0]) || (idx
< 0) || (idx
>= count
))
6994 reply("CSMSG_INVALID_NUMERIC", idx
);
6995 /* Show possible values. */
6996 for(idx
= 0; idx
< count
; idx
++)
6997 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
7001 cData
->chOpts
[option
] = charOptions
[option
].values
[idx
].value
;
7005 /* Find current option value. */
7008 (idx
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[idx
].value
);
7012 /* Somehow, the option value is corrupt; reset it to the default. */
7013 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
7018 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
7022 static MODCMD_FUNC(chan_opt_automode
)
7024 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
7027 static MODCMD_FUNC(chan_opt_protect
)
7029 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
7032 static MODCMD_FUNC(chan_opt_toys
)
7034 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
7037 static MODCMD_FUNC(chan_opt_ctcpreaction
)
7039 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
7042 static MODCMD_FUNC(chan_opt_bantimeout
)
7044 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
7047 static MODCMD_FUNC(chan_opt_topicrefresh
)
7049 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
7052 static MODCMD_FUNC(chan_opt_resync
)
7054 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
7057 static struct svccmd_list set_shows_list
;
7060 handle_svccmd_unbind(struct svccmd
*target
, UNUSED_ARG(void *extra
)) {
7062 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
7063 if(target
== set_shows_list
.list
[ii
])
7064 set_shows_list
.used
= 0;
7067 static CHANSERV_FUNC(cmd_set
)
7069 struct svccmd
*subcmd
;
7073 /* Check if we need to (re-)initialize set_shows_list. */
7074 if(!set_shows_list
.used
)
7076 if(!set_shows_list
.size
)
7078 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
7079 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
7081 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
7083 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
7084 sprintf(buf
, "%s %s", argv
[0], name
);
7085 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7088 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
7091 svccmd_list_append(&set_shows_list
, subcmd
);
7097 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
7098 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
7100 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
7102 subcmd
= set_shows_list
.list
[ii
];
7103 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
7105 reply("CSMSG_CHANNEL_OPTIONS_END");
7109 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7110 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7113 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7116 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
7118 reply("CSMSG_NO_ACCESS");
7124 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7128 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
7130 struct userData
*uData
;
7132 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7135 reply("CSMSG_NOT_USER", channel
->name
);
7141 /* Just show current option value. */
7143 else if(enabled_string(argv
[1]))
7145 uData
->flags
|= mask
;
7147 else if(disabled_string(argv
[1]))
7149 uData
->flags
&= ~mask
;
7153 reply("MSG_INVALID_BINARY", argv
[1]);
7157 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
7161 static MODCMD_FUNC(user_opt_autoop
)
7163 struct userData
*uData
;
7165 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7168 reply("CSMSG_NOT_USER", channel
->name
);
7171 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
7172 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
7174 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
7177 static MODCMD_FUNC(user_opt_autoinvite
)
7179 if((argc
> 1) && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
7181 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
7183 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
7186 static MODCMD_FUNC(user_opt_autojoin
)
7188 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
7191 static MODCMD_FUNC(user_opt_info
)
7193 struct userData
*uData
;
7196 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7200 /* If they got past the command restrictions (which require access)
7201 * but fail this test, we have some fool with security override on.
7203 reply("CSMSG_NOT_USER", channel
->name
);
7210 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
7211 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
7213 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
7216 bp
= strcspn(infoline
, "\001");
7219 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
7224 if(infoline
[0] == '*' && infoline
[1] == 0)
7227 uData
->info
= strdup(infoline
);
7230 reply("CSMSG_USET_INFO", uData
->info
);
7232 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
7236 struct svccmd_list uset_shows_list
;
7238 static CHANSERV_FUNC(cmd_uset
)
7240 struct svccmd
*subcmd
;
7244 /* Check if we need to (re-)initialize uset_shows_list. */
7245 if(!uset_shows_list
.used
)
7249 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7252 if(!uset_shows_list
.size
)
7254 uset_shows_list
.size
= ArrayLength(options
);
7255 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
7257 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
7259 const char *name
= options
[ii
];
7260 sprintf(buf
, "%s %s", argv
[0], name
);
7261 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7264 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
7267 svccmd_list_append(&uset_shows_list
, subcmd
);
7273 /* Do this so options are presented in a consistent order. */
7274 reply("CSMSG_USER_OPTIONS");
7275 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
7276 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
7280 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7281 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7284 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7288 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7291 static CHANSERV_FUNC(cmd_giveownership
)
7293 struct handle_info
*new_owner_hi
;
7294 struct userData
*new_owner
;
7295 struct userData
*curr_user
;
7296 struct userData
*invoker
;
7297 struct chanData
*cData
= channel
->channel_info
;
7298 struct do_not_register
*dnr
;
7299 struct giveownership
*giveownership
;
7300 const char *confirm
;
7301 unsigned int force
, override
;
7302 unsigned short co_access
, new_owner_old_access
;
7303 char transfer_reason
[MAXLEN
];
7306 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
7307 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
7309 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
7310 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
7311 && (uData
->access
> 500)
7312 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
7313 || uData
->access
< 500));
7316 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
7318 struct userData
*owner
= NULL
;
7319 for(curr_user
= channel
->channel_info
->users
;
7321 curr_user
= curr_user
->next
)
7323 if(curr_user
->access
!= UL_OWNER
)
7327 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
7334 else if(!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
7336 char delay
[INTERVALLEN
];
7337 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
7338 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
7342 reply("CSMSG_NO_OWNER", channel
->name
);
7345 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
7347 if(new_owner_hi
== user
->handle_info
)
7349 reply("CSMSG_NO_TRANSFER_SELF");
7352 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
7357 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_OWNER
- 1, 0, NULL
, 0);
7361 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
7365 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
7367 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
7370 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
7371 if(!IsHelping(user
))
7372 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
7374 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
7378 invoker
= GetChannelUser(cData
, user
->handle_info
);
7379 if(invoker
->access
<= UL_OWNER
)
7381 confirm
= make_confirmation_string(curr_user
);
7382 if((argc
< 3) || strcmp(argv
[2], confirm
))
7384 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi
->handle
, confirm
);
7389 new_owner_old_access
= new_owner
->access
;
7390 if(new_owner
->access
>= UL_COOWNER
)
7391 co_access
= new_owner
->access
;
7393 co_access
= UL_COOWNER
;
7394 new_owner
->access
= UL_OWNER
;
7396 curr_user
->access
= co_access
;
7397 cData
->ownerTransfer
= now
;
7399 giveownership
= calloc(1, sizeof(*giveownership
));
7400 giveownership
->issued
= now
;
7401 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
7402 giveownership
->target
= strdup(new_owner_hi
->handle
);
7403 giveownership
->target_access
= new_owner_old_access
;
7406 if(argc
> (2 + force
))
7408 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
7409 giveownership
->reason
= strdup(transfer_reason
);
7411 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
7414 giveownership
->previous
= channel
->channel_info
->giveownership
;
7415 channel
->channel_info
->giveownership
= giveownership
;
7417 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
7418 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
7419 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
7424 chanserv_expire_user_suspension(void *data
)
7426 struct userData
*target
= data
;
7428 target
->expires
= 0;
7429 target
->flags
&= ~USER_SUSPENDED
;
7432 static CHANSERV_FUNC(cmd_suspend
)
7434 struct handle_info
*hi
;
7435 struct userData
*actor
, *real_actor
, *target
;
7436 unsigned int override
= 0;
7440 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7441 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7442 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7443 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7445 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7448 if(target
->access
>= actor
->access
)
7450 reply("MSG_USER_OUTRANKED", hi
->handle
);
7453 if(target
->flags
& USER_SUSPENDED
)
7455 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
7460 target
->present
= 0;
7463 if(!strcmp(argv
[2], "0"))
7467 unsigned int duration
;
7468 if(!(duration
= ParseInterval(argv
[2])))
7470 reply("MSG_INVALID_DURATION", argv
[2]);
7473 expiry
= now
+ duration
;
7476 target
->expires
= expiry
;
7479 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
7481 if(!real_actor
|| target
->access
>= real_actor
->access
)
7482 override
= CMD_LOG_OVERRIDE
;
7483 target
->flags
|= USER_SUSPENDED
;
7484 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
7485 return 1 | override
;
7488 static CHANSERV_FUNC(cmd_unsuspend
)
7490 struct handle_info
*hi
;
7491 struct userData
*actor
= NULL
, *real_actor
= NULL
, *target
;
7492 unsigned int override
= 0;
7495 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7496 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7497 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7498 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7500 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7503 if(target
->access
>= actor
->access
)
7505 reply("MSG_USER_OUTRANKED", hi
->handle
);
7508 if(!(target
->flags
& USER_SUSPENDED
))
7510 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
7513 if(!real_actor
|| target
->access
>= real_actor
->access
)
7514 override
= CMD_LOG_OVERRIDE
;
7515 target
->flags
&= ~USER_SUSPENDED
;
7516 scan_user_presence(target
, NULL
);
7517 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
7518 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
7519 return 1 | override
;
7522 static MODCMD_FUNC(cmd_deleteme
)
7524 struct handle_info
*hi
;
7525 struct userData
*target
;
7526 const char *confirm_string
;
7527 unsigned short access
;
7530 hi
= user
->handle_info
;
7531 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7533 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7536 if(target
->access
== UL_OWNER
)
7538 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
7541 confirm_string
= make_confirmation_string(target
);
7542 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
7544 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
7547 access
= target
->access
;
7548 channel_name
= strdup(channel
->name
);
7549 del_channel_user(target
, 1);
7550 reply("CSMSG_DELETED_YOU", access
, channel_name
);
7556 chanserv_refresh_topics(UNUSED_ARG(void *data
))
7558 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7559 struct chanData
*cData
;
7562 for(cData
= channelList
; cData
; cData
= cData
->next
)
7564 if(IsSuspended(cData
))
7566 opt
= cData
->chOpts
[chTopicRefresh
];
7569 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
7572 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
7573 cData
->last_refresh
= refresh_num
;
7575 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
7579 chanserv_auto_resync(UNUSED_ARG(void *data
))
7581 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7582 struct chanData
*cData
;
7585 for(cData
= channelList
; cData
; cData
= cData
->next
)
7587 if(IsSuspended(cData
)) continue;
7588 opt
= cData
->chOpts
[chResync
];
7589 if(opt
== 'n') continue;
7590 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
7591 resync_channel(cData
->channel
);
7592 cData
->last_resync
= refresh_num
;
7594 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7597 static CHANSERV_FUNC(cmd_unf
)
7601 char response
[MAXLEN
];
7602 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7603 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7604 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7607 reply("CSMSG_UNF_RESPONSE");
7611 static CHANSERV_FUNC(cmd_ping
)
7615 char response
[MAXLEN
];
7616 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7617 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7618 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7621 reply("CSMSG_PING_RESPONSE");
7625 static CHANSERV_FUNC(cmd_wut
)
7629 char response
[MAXLEN
];
7630 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7631 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7632 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7635 reply("CSMSG_WUT_RESPONSE");
7639 static CHANSERV_FUNC(cmd_roulette
)
7642 struct chanData
*cData
= channel
->channel_info
;
7645 if (cData
->roulette_chamber
) {
7646 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7650 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7651 cData
->roulette_chamber
= 1 + rand() % 6;
7657 static CHANSERV_FUNC(cmd_shoot
)
7660 struct chanData
*cData
= channel
->channel_info
;
7662 if (cData
->roulette_chamber
<= 0) {
7663 struct service
*service
;
7664 if ((service
= service_find(chanserv
->nick
))) {
7665 reply("CSMSG_ROULETTE_NEW", service
->trigger
);
7670 cData
->roulette_chamber
--;
7672 if (cData
->roulette_chamber
== 0) {
7673 reply("CSMSG_ROULETTE_BANG");
7674 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7675 DelUser(user
, chanserv
, 1, "BANG!!!!");
7677 reply("CSMSG_ROULETTE_CLICK");
7684 chanserv_remove_abuse(void *data
)
7686 char *remnick
= data
;
7687 struct userNode
*user
;
7688 /* sometimes the clone was killed and maybe even the user took their nick back
7689 * (ie, an oper) so dont kill them here after all unless they are local. */
7690 if( (user
= GetUserH(remnick
)) )
7692 DelUser(user
, NULL
, 1, "");
7695 int lamepart(struct userNode
*nick
) {
7696 struct modeNode
*mn
;
7697 unsigned int count
, n
;
7699 for (n
=count
=0; n
<nick
->channels
.used
; n
++) {
7700 mn
= nick
->channels
.list
[n
];
7701 irc_svspart(chanserv
, nick
, mn
->channel
);
7707 static CHANSERV_FUNC(cmd_spin
)
7712 int type
= 0, lamep
= 1;
7715 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7723 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7724 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7725 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7727 if(chanserv_conf
.wheel
->used
< 1) {
7728 /* wheel actions not defined! eek */
7732 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7733 if(!wheel
&& *wheel
)
7736 /* enable this to be able to manually specify a result for testing:
7737 log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
7743 /* connection reset by peer */
7744 if (!strcasecmp(wheel
, "peer")) {
7745 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7747 irc_kill(chanserv
, user
, "Connection reset by peer");
7749 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7751 /* part all channels */
7752 else if (!strcasecmp(wheel
, "partall")) {
7753 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7757 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7759 /* random time gline */
7760 else if (!strcasecmp(wheel
, "gline")) {
7761 char target
[HOSTLEN
+ 3];
7762 int wtime
= 120 + rand() % 600;
7764 strcpy(target
, "*@");
7765 strcat(target
, user
->hostname
);
7766 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7768 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1, 0);
7769 // irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7772 else if (!strcasecmp(wheel
, "shun")) {
7773 char target
[HOSTLEN
+ 3];
7774 int wtime
= 120 + rand() % 600;
7776 strcpy(target
, "*@");
7777 strcat(target
, user
->hostname
);
7778 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SHUN");
7780 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1);
7782 /* absolutely nothing */
7783 else if (!strcasecmp(wheel
, "nothing")) {
7784 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7786 /* join random chans and part em several times */
7787 else if (!strcasecmp(wheel
, "randjoin")) {
7793 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7794 while(complete
!= 1) {
7795 if (rndchans
!= 15) {
7796 chango
= 120 + rand() % 600;
7797 sputsock("%s SJ %s #%d "FMT_TIME_T
, self
->numeric
, user
->numeric
, chango
, now
);
7800 if (roundz0r
!= 1) {
7804 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7811 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7817 /* abuse line added to /whois */
7818 else if (!strcasecmp(wheel
, "abusewhois")) {
7819 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7820 irc_swhois(chanserv
, user
, "is being defecated on by services");
7822 /* kick from each channel your in */
7823 else if (!strcasecmp(wheel
, "kickall")) {
7824 unsigned int count
, n
;
7825 struct modeNode
*mn
;
7827 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7829 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7830 mn
= user
->channels
.list
[n
];
7831 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7834 /* random nick change */
7835 else if (!strcasecmp(wheel
, "nickchange")) {
7836 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7838 char *oldnick
= NULL
;
7839 char *oldident
= NULL
;
7840 char abusednick
[NICKLEN
] = "";
7841 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7842 struct userNode
*clone
;
7844 oldnick
= strdup(user
->nick
);
7845 oldident
= strdup(user
->ident
);
7847 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7849 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7850 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7851 if(!GetUserH(abusednick
))
7855 SVSNickChange(user
, abusednick
);
7856 irc_svsnick(chanserv
, user
, abusednick
);
7857 clone
= AddLocalUser(oldnick
, oldident
, "abused.by.wheel.of.misfortune", "I got abused by the wheel of misfortune :D", "+i");
7858 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7861 else if (!strcasecmp(wheel
, "kill")) {
7862 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7864 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7865 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7867 /* service ignore */
7868 else if (!strcasecmp(wheel
, "svsignore")) {
7869 int gagged
, ignoretime
= 0;
7870 char target
[HOSTLEN
+ 13];
7873 /* we cant gag opers, so just verbally abuse them */
7874 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7877 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7879 strcpy(target
, "*!*@");
7880 strcat(target
, user
->hostname
);
7881 ignoretime
= now
+ (1 + rand() % 120);
7883 gagged
= gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7885 /* kick and ban from each channel your in */
7886 else if (!strcasecmp(wheel
, "kickbanall")) {
7887 unsigned int count
, n
;
7888 struct modeNode
*mn
;
7889 //char ban[HOSTLEN + 1];
7891 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7893 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7894 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7895 struct mod_chanmode
*change
;
7896 /* struct banData *bData; */
7897 unsigned int exists
;
7898 /* int duration = 300; */
7901 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7903 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7904 mn
= user
->channels
.list
[n
];
7905 if(mn
->channel
->banlist
.used
>= MAXBANS
) {
7906 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7911 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7913 change
= mod_chanmode_alloc(1);
7914 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7915 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7918 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7919 mod_chanmode_free(change
);
7921 exists
= ChannelBanExists(mn
->channel
, ban
);
7923 change
= mod_chanmode_alloc(1);
7924 change
->args
[0].mode
= MODE_BAN
;
7925 change
->args
[0].u
.hostmask
= ban
;
7927 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7928 mod_chanmode_free(change
);
7932 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7936 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7940 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7947 static CHANSERV_FUNC(cmd_8ball
)
7949 unsigned int i
, j
, accum
;
7954 for(i
=1; i
<argc
; i
++)
7955 for(j
=0; argv
[i
][j
]; j
++)
7956 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7957 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7960 char response
[MAXLEN
];
7961 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7962 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7965 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7969 #else /* Use cool 8ball instead */
7971 void eightball(char *outcome
, int method
, unsigned int seed
)
7975 #define NUMOFCOLORS 18
7976 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7977 "white", "black", "grey", "brown",
7978 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7979 "fuchsia","turquoise","magenta", "cyan"};
7980 #define NUMOFLOCATIONS 50
7981 char balllocations
[50][55] = {
7982 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7983 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7984 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7985 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7986 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7987 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7988 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7989 "your bra", "your hair", "your bed", "the couch", "the wall",
7990 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7991 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7992 #define NUMOFPREPS 15
7993 char ballpreps
[50][50] = {
7994 "Near", "Somewhere near", "In", "In", "In",
7995 "In", "Hiding in", "Under", "Next to", "Over",
7996 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7997 #define NUMOFNUMS 34
7998 char ballnums
[50][50] = {
7999 "A hundred", "A thousand", "A few", "42",
8000 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
8001 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8002 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8004 #define NUMOFMULTS 8
8005 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
8008 * 0: normal (Not used in x3)
8015 if (method
== 1) /* A Color */
8019 answer
= (rand() % 12); /* Make sure this is the # of entries */
8022 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
8024 case 1: strcpy(tmp
, "Sort of a light %s color.");
8026 case 2: strcpy(tmp
, "Dark and dreary %s.");
8028 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
8030 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
8032 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
8034 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
8036 case 10: strcpy(tmp
, "Solid %s.");
8038 case 11: strcpy(tmp
, "Transparent %s.");
8040 default: strcpy(outcome
, "An invalid random number was generated.");
8043 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
8046 else if (method
== 2) /* Location */
8048 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
8050 else if (method
== 3) /* Number of ___ */
8052 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
8056 //Debug(DBGWARNING, "Error in 8ball.");
8061 static CHANSERV_FUNC(cmd_8ball
)
8063 char *word1
, *word2
, *word3
;
8064 static char eb
[MAXLEN
];
8065 unsigned int accum
, i
, j
;
8069 for(i
=1; i
<argc
; i
++)
8070 for(j
=0; argv
[i
][j
]; j
++)
8071 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
8073 accum
+= time(NULL
)/3600;
8075 word2
= argc
>2?argv
[2]:"";
8076 word3
= argc
>3?argv
[3]:"";
8079 if((word2
) && strcasecmp(word1
, "what") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8080 eightball(eb
, 1, accum
);
8081 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8082 eightball(eb
, 1, accum
);
8083 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8084 eightball(eb
, 1, accum
);
8085 /*** LOCATION *****/
8090 (strcasecmp(word1
, "where") == 0) &&
8091 (strcasecmp(word2
, "is") == 0)
8095 strcasecmp(word1
, "where's") == 0
8098 eightball(eb
, 2, accum
);
8100 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
8101 eightball(eb
, 3, accum
);
8105 /* Generic 8ball question.. so pull from x3.conf srvx style */
8108 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
8111 char response
[MAXLEN
];
8112 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
8113 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8116 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
8122 char response
[MAXLEN
];
8123 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
8124 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8127 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
8132 static CHANSERV_FUNC(cmd_d
)
8134 unsigned long sides
, count
, modifier
, ii
, total
;
8135 char response
[MAXLEN
], *sep
;
8139 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
8149 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
8150 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
8154 else if((sep
[0] == '-') && isdigit(sep
[1]))
8155 modifier
= strtoul(sep
, NULL
, 10);
8156 else if((sep
[0] == '+') && isdigit(sep
[1]))
8157 modifier
= strtoul(sep
+1, NULL
, 10);
8164 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
8169 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
8172 for(total
= ii
= 0; ii
< count
; ++ii
)
8173 total
+= (rand() % sides
) + 1;
8176 if((count
> 1) || modifier
)
8178 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
8179 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
8183 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
8184 sprintf(response
, fmt
, total
, sides
);
8187 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8189 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8193 static CHANSERV_FUNC(cmd_huggle
)
8195 /* CTCP must be via PRIVMSG, never notice */
8197 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
8199 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
8203 static CHANSERV_FUNC(cmd_calc
)
8205 char response
[MAXLEN
];
8208 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8211 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8213 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8217 static CHANSERV_FUNC(cmd_reply
)
8221 unsplit_string(argv
+ 1, argc
- 1, NULL
);
8224 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8226 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
8231 chanserv_adjust_limit(void *data
)
8233 struct mod_chanmode change
;
8234 struct chanData
*cData
= data
;
8235 struct chanNode
*channel
= cData
->channel
;
8238 if(IsSuspended(cData
))
8241 cData
->limitAdjusted
= now
;
8242 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
8243 if(cData
->modes
.modes_set
& MODE_LIMIT
)
8245 if(limit
> cData
->modes
.new_limit
)
8246 limit
= cData
->modes
.new_limit
;
8247 else if(limit
== cData
->modes
.new_limit
)
8251 mod_chanmode_init(&change
);
8252 change
.modes_set
= MODE_LIMIT
;
8253 change
.new_limit
= limit
;
8254 mod_chanmode_announce(chanserv
, channel
, &change
);
8258 handle_new_channel(struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8260 struct chanData
*cData
;
8262 if(!(cData
= channel
->channel_info
))
8265 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
8266 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
8268 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
8269 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
8273 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
8275 struct banData
*bData
;
8276 struct mod_chanmode
*change
;
8278 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
8283 if (chan
->channel_info
) {
8284 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
8286 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0))
8298 check_bans(struct userNode
*user
, const char *channel
)
8300 struct chanNode
*chan
;
8301 struct mod_chanmode change
;
8302 struct chanData
*cData
;
8303 struct banData
*bData
;
8305 if (!(chan
= GetChannel(channel
)))
8308 if(!(cData
= chan
->channel_info
))
8311 mod_chanmode_init(&change
);
8314 if(chan
->banlist
.used
< MAXBANS
)
8316 /* Not joining through a ban. */
8317 for(bData
= cData
->bans
;
8318 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8319 bData
= bData
->next
);
8323 char kick_reason
[MAXLEN
];
8324 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8326 bData
->triggered
= now
;
8327 if(bData
!= cData
->bans
)
8329 /* Shuffle the ban to the head of the list. */
8331 bData
->next
->prev
= bData
->prev
;
8333 bData
->prev
->next
= bData
->next
;
8336 bData
->next
= cData
->bans
;
8339 cData
->bans
->prev
= bData
;
8341 cData
->bans
= bData
;
8344 change
.args
[0].mode
= MODE_BAN
;
8345 change
.args
[0].u
.hostmask
= bData
->mask
;
8346 mod_chanmode_announce(chanserv
, chan
, &change
);
8347 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8355 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
8358 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
8360 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
, 0))
8367 /* Welcome to my worst nightmare. Warning: Read (or modify)
8368 the code below at your own risk. */
8370 handle_join(struct modeNode
*mNode
, UNUSED_ARG(void *extra
))
8372 struct mod_chanmode change
;
8373 struct userNode
*user
= mNode
->user
;
8374 struct chanNode
*channel
= mNode
->channel
;
8375 struct chanData
*cData
;
8376 struct userData
*uData
= NULL
;
8377 struct banData
*bData
;
8378 struct handle_info
*handle
;
8379 unsigned int modes
= 0, info
= 0;
8382 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8385 cData
= channel
->channel_info
;
8386 if(channel
->members
.used
> cData
->max
)
8387 cData
->max
= channel
->members
.used
;
8390 /* Check for bans. If they're joining through a ban, one of two
8392 * 1: Join during a netburst, by riding the break. Kick them
8393 * unless they have ops or voice in the channel.
8394 * 2: They're allowed to join through the ban (an invite in
8395 * ircu2.10, or a +e on Hybrid, or something).
8396 * If they're not joining through a ban, and the banlist is not
8397 * full, see if they're on the banlist for the channel. If so,
8400 if(user
->uplink
->burst
&& !mNode
->modes
)
8403 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
8405 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
, 0))
8407 /* Riding a netburst. Naughty. */
8408 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
8415 if(user
->handle_info
)
8417 handle
= user
->handle_info
;
8420 uData
= GetTrueChannelAccess(cData
, handle
);
8425 mod_chanmode_init(&change
);
8428 /* TODO: maybe only people above inviteme level? -Rubin */
8429 /* We don't kick people with access */
8430 if(!uData
&& !channel_user_is_exempt(user
, channel
))
8432 if(channel
->banlist
.used
< MAXBANS
)
8434 /* Not joining through a ban. */
8435 for(bData
= cData
->bans
;
8436 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8437 bData
= bData
->next
);
8441 char kick_reason
[MAXLEN
];
8442 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8444 bData
->triggered
= now
;
8445 if(bData
!= cData
->bans
)
8447 /* Shuffle the ban to the head of the list. */
8449 bData
->next
->prev
= bData
->prev
;
8451 bData
->prev
->next
= bData
->next
;
8454 bData
->next
= cData
->bans
;
8457 cData
->bans
->prev
= bData
;
8458 cData
->bans
= bData
;
8461 change
.args
[0].mode
= MODE_BAN
;
8462 change
.args
[0].u
.hostmask
= bData
->mask
;
8463 mod_chanmode_announce(chanserv
, channel
, &change
);
8464 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8470 /* ChanServ will not modify the limits in join-flooded channels.
8471 It will also skip DynLimit processing when the user (or srvx)
8472 is bursting in, because there are likely more incoming. */
8473 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8474 && !user
->uplink
->burst
8475 && !channel
->join_flooded
8476 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
8478 /* The user count has begun "bumping" into the channel limit,
8479 so set a timer to raise the limit a bit. Any previous
8480 timers are removed so three incoming users within the delay
8481 results in one limit change, not three. */
8483 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8484 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8487 /* Give automodes exept during join-floods */
8488 if(!channel
->join_flooded
)
8490 if(cData
->chOpts
[chAutomode
] == 'v')
8491 modes
|= MODE_VOICE
;
8492 else if(cData
->chOpts
[chAutomode
] == 'h')
8493 modes
|= MODE_HALFOP
;
8494 else if(cData
->chOpts
[chAutomode
] == 'o')
8495 modes
|= MODE_CHANOP
;
8498 greeting
= cData
->greeting
;
8499 if(user
->handle_info
)
8501 /* handle = user->handle_info; */
8503 if(IsHelper(user
) && !IsHelping(user
))
8506 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8508 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
8510 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8516 /* uData = GetTrueChannelAccess(cData, handle); */
8517 if(uData
&& !IsUserSuspended(uData
))
8519 /* non users getting automodes are handled above. */
8520 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
8522 /* just op everyone with access */
8523 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8524 modes
|= MODE_VOICE
;
8525 /* or do their access level */
8526 else if(uData
->access
>= UL_OP
)
8527 modes
|= MODE_CHANOP
;
8528 else if(uData
->access
>= UL_HALFOP
)
8529 modes
|= MODE_HALFOP
;
8530 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
8531 modes
|= MODE_VOICE
;
8533 if(uData
->access
>= UL_PRESENT
)
8534 cData
->visited
= now
;
8535 if(cData
->user_greeting
)
8536 greeting
= cData
->user_greeting
;
8538 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
8539 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
8547 /* If user joining normally (not during burst), apply op or voice,
8548 * and send greeting/userinfo as appropriate.
8550 if(!user
->uplink
->burst
)
8554 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8555 if(modes & MODE_CHANOP) {
8556 modes &= ~MODE_HALFOP;
8557 modes &= ~MODE_VOICE;
8560 change
.args
[0].mode
= modes
;
8561 change
.args
[0].u
.member
= mNode
;
8562 mod_chanmode_announce(chanserv
, channel
, &change
);
8565 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
8567 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
8573 chanserv_autojoin_channels(struct userNode
*user
)
8575 struct userData
*channel
;
8577 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8579 struct chanNode
*cn
;
8580 struct modeNode
*mn
;
8582 if(IsUserSuspended(channel
)
8583 || IsSuspended(channel
->channel
)
8584 || !(cn
= channel
->channel
->channel
))
8587 mn
= GetUserMode(cn
, user
);
8590 if(!IsUserSuspended(channel
)
8591 && IsUserAutoJoin(channel
)
8592 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8594 && !user
->uplink
->burst
)
8595 irc_svsjoin(chanserv
, user
, cn
);
8601 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
), UNUSED_ARG(void *extra
))
8603 struct mod_chanmode change
;
8604 struct userData
*channel
;
8605 unsigned int ii
, jj
, i
;
8607 if(!user
->handle_info
)
8610 mod_chanmode_init(&change
);
8612 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8614 struct chanNode
*cn
;
8615 struct chanData
*cData
;
8616 struct modeNode
*mn
;
8617 if(IsUserSuspended(channel
)
8618 || IsSuspended(channel
->channel
)
8619 || !(cn
= channel
->channel
->channel
))
8622 cData
= cn
->channel_info
;
8623 mn
= GetUserMode(cn
, user
);
8626 if(!IsUserSuspended(channel
)
8627 && IsUserAutoInvite(channel
)
8628 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8630 && !user
->uplink
->burst
)
8631 irc_invite(chanserv
, user
, cn
);
8635 if(channel
->access
>= UL_PRESENT
)
8636 channel
->channel
->visited
= now
;
8638 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8640 if(channel
->access
>= UL_OP
)
8641 change
.args
[0].mode
= MODE_CHANOP
;
8642 else if(channel
->access
>= UL_HALFOP
)
8643 change
.args
[0].mode
= MODE_HALFOP
;
8644 else if(channel
->access
>= UL_PEON
)
8645 change
.args
[0].mode
= MODE_VOICE
;
8647 change
.args
[0].mode
= 0;
8648 change
.args
[0].u
.member
= mn
;
8649 if(change
.args
[0].mode
)
8650 mod_chanmode_announce(chanserv
, cn
, &change
);
8653 channel
->seen
= now
;
8654 channel
->present
= 1;
8657 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8659 struct chanNode
*chan
= user
->channels
.list
[ii
]->channel
;
8660 struct banData
*ban
;
8662 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8663 || !chan
->channel_info
8664 || IsSuspended(chan
->channel_info
))
8666 if(protect_user(user
, chanserv
, chan
->channel_info
, true))
8668 for(jj
= 0; jj
< chan
->banlist
.used
; ++jj
)
8669 if(user_matches_glob(user
, chan
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8671 if(jj
< chan
->banlist
.used
)
8673 for(ban
= chan
->channel_info
->bans
; ban
; ban
= ban
->next
)
8675 char kick_reason
[MAXLEN
];
8676 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
, 0))
8678 change
.args
[0].mode
= MODE_BAN
;
8679 change
.args
[0].u
.hostmask
= ban
->mask
;
8680 mod_chanmode_announce(chanserv
, chan
, &change
);
8681 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8682 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8683 ban
->triggered
= now
;
8688 if(IsSupportHelper(user
))
8690 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8692 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8694 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8700 if (user
->handle_info
->ignores
->used
) {
8701 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8702 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8706 if (user
->handle_info
->epithet
)
8707 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8709 /* process autojoin channels 5 seconds later as this sometimes
8710 happens before autohide */
8711 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8712 chanserv_autojoin_channels(user
);
8716 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
), UNUSED_ARG(void *extra
))
8718 struct chanData
*cData
;
8719 struct userData
*uData
;
8721 cData
= mn
->channel
->channel_info
;
8722 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8725 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8727 /* Allow for a bit of padding so that the limit doesn't
8728 track the user count exactly, which could get annoying. */
8729 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8731 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8732 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8736 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8738 scan_user_presence(uData
, mn
->user
);
8740 if (uData
->access
>= UL_PRESENT
)
8741 cData
->visited
= now
;
8744 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8747 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
) {
8748 struct chanNode
*channel
;
8749 struct userNode
*exclude
;
8750 /* When looking at the channel that is being /part'ed, we
8751 * have to skip over the client that is leaving. For
8752 * other channels, we must not do that.
8754 channel
= chanserv_conf
.support_channels
.list
[ii
];
8755 exclude
= (channel
== mn
->channel
) ? mn
->user
: NULL
;
8756 if(find_handle_in_channel(channel
, mn
->user
->handle_info
, exclude
))
8759 if(ii
== chanserv_conf
.support_channels
.used
)
8760 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8765 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8767 struct userData
*uData
;
8769 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8770 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8771 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8774 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8776 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8777 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8780 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8785 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
, UNUSED_ARG(void *extra
))
8787 struct chanData
*cData
;
8789 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8792 cData
= channel
->channel_info
;
8793 if(bad_topic(channel
, user
, channel
->topic
))
8794 { /* User doesnt have privs to set topics. Undo it */
8795 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8796 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8799 /* If there is a topic mask set, and the new topic doesnt match,
8800 * set the topic to mask + new_topic */
8801 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8803 char new_topic
[TOPICLEN
+1];
8804 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8807 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8808 /* and fall through to topicsnarf code below.. */
8810 else /* Topic couldnt fit into mask, was too long */
8812 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8813 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8814 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8818 /* With topicsnarf, grab the topic and save it as the default topic. */
8819 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8822 cData
->topic
= strdup(channel
->topic
);
8828 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
, UNUSED_ARG(void *extra
))
8830 struct mod_chanmode
*bounce
= NULL
;
8831 unsigned int bnc
, ii
;
8834 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8837 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8838 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8840 char correct
[MAXLEN
];
8841 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8842 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8843 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8845 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8847 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8849 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8850 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8853 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8856 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8857 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8858 if(bounce
->args
[bnc
].u
.member
)
8862 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8863 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8865 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8867 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8869 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8870 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8873 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8874 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8875 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8878 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8880 const char *ban
= change
->args
[ii
].u
.hostmask
;
8881 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8884 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8885 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8886 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8888 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8893 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8894 mod_chanmode_announce(chanserv
, channel
, bounce
);
8895 for(ii
= 0; ii
< change
->argc
; ++ii
)
8896 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8897 free((char*)bounce
->args
[ii
].u
.hostmask
);
8898 mod_chanmode_free(bounce
);
8903 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
), UNUSED_ARG(void *extra
))
8905 struct chanNode
*channel
;
8906 struct banData
*bData
;
8907 struct mod_chanmode change
;
8908 unsigned int ii
, jj
;
8909 char kick_reason
[MAXLEN
];
8911 mod_chanmode_init(&change
);
8913 change
.args
[0].mode
= MODE_BAN
;
8914 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8916 channel
= user
->channels
.list
[ii
]->channel
;
8917 /* Need not check for bans if they're opped or voiced. */
8918 /* TODO: does this make sense in automode v, h, and o? *
8919 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8920 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8922 /* Need not check for bans unless channel registration is active. */
8923 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8925 /* Look for a matching ban already on the channel. */
8926 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8927 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8929 /* Need not act if we found one. */
8930 if(jj
< channel
->banlist
.used
)
8932 /* don't kick someone on the userlist */
8933 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8935 /* Look for a matching ban in this channel. */
8936 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8938 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
8940 change
.args
[0].u
.hostmask
= bData
->mask
;
8941 mod_chanmode_announce(chanserv
, channel
, &change
);
8942 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8943 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8944 bData
->triggered
= now
;
8945 break; /* we don't need to check any more bans in the channel */
8950 static void handle_rename(struct handle_info
*handle
, const char *old_handle
, UNUSED_ARG(void *extra
))
8952 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8956 dict_remove2(handle_dnrs
, old_handle
, 1);
8957 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8958 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8963 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
, UNUSED_ARG(void *extra
))
8965 struct userNode
*h_user
;
8967 if(handle
->channels
)
8969 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8970 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8972 while(handle
->channels
)
8973 del_channel_user(handle
->channels
, 1);
8978 handle_server_link(UNUSED_ARG(struct server
*server
), UNUSED_ARG(void *extra
))
8980 struct chanData
*cData
;
8982 for(cData
= channelList
; cData
; cData
= cData
->next
)
8984 if(!IsSuspended(cData
))
8985 cData
->may_opchan
= 1;
8986 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8987 && !cData
->channel
->join_flooded
8988 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8989 < chanserv_conf
.adjust_threshold
))
8991 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8992 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8999 chanserv_conf_read(void)
9003 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
9004 struct mod_chanmode
*change
;
9005 struct string_list
*strlist
;
9006 struct chanNode
*chan
;
9009 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
9011 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
9014 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9015 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9016 chanserv_conf
.support_channels
.used
= 0;
9017 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
9019 for(ii
= 0; ii
< strlist
->used
; ++ii
)
9021 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9024 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
9026 channelList_append(&chanserv_conf
.support_channels
, chan
);
9029 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
9032 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9035 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
9037 channelList_append(&chanserv_conf
.support_channels
, chan
);
9039 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
9040 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
9041 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
9042 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
9043 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
9044 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
9045 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
9046 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
9047 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
9048 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
9049 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
9050 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
9051 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
9052 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
9053 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
9054 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
9055 str
= database_get_data(conf_node
, KEY_DNR_EXPIRE_FREQ
, RECDB_QSTRING
);
9056 chanserv_conf
.dnr_expire_frequency
= str
? ParseInterval(str
) : 3600;
9057 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
9058 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
9059 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
9060 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
9061 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
9062 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
9063 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
9064 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
9065 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
9067 NickChange(chanserv
, str
, 0);
9068 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
9069 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
9070 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
9071 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
9072 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
9073 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
9074 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
9075 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
9076 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
9077 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
9078 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
9079 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
9080 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
9081 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
9082 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
9083 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
9084 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
9085 god_timeout
= str
? ParseInterval(str
) : 60*15;
9086 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
9089 safestrncpy(mode_line
, str
, sizeof(mode_line
));
9090 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
9091 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
9092 && (change
->argc
< 2))
9094 chanserv_conf
.default_modes
= *change
;
9095 mod_chanmode_free(change
);
9097 str
= database_get_data(conf_node
, KEY_VALID_CHANNEL_REGEX
, RECDB_QSTRING
);
9098 if (chanserv_conf
.valid_channel_regex_set
)
9099 regfree(&chanserv_conf
.valid_channel_regex
);
9101 int err
= regcomp(&chanserv_conf
.valid_channel_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
9102 chanserv_conf
.valid_channel_regex_set
= !err
;
9103 if (err
) log_module(CS_LOG
, LOG_ERROR
, "Bad valid_channel_regex (error %d)", err
);
9105 chanserv_conf
.valid_channel_regex_set
= 0;
9107 free_string_list(chanserv_conf
.wheel
);
9108 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
9110 strlist
= string_list_copy(strlist
);
9113 static const char *list
[] = {
9114 "peer", "partall", "gline", /* "shun", */
9115 "nothing", "randjoin", "abusewhois", "kickall",
9116 "nickchange", "kill", "svsignore", "kickbanall",
9119 strlist
= alloc_string_list(ArrayLength(list
)-1);
9120 for(ii
=0; list
[ii
]; ii
++)
9121 string_list_append(strlist
, strdup(list
[ii
]));
9123 chanserv_conf
.wheel
= strlist
;
9125 free_string_list(chanserv_conf
.set_shows
);
9126 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
9128 strlist
= string_list_copy(strlist
);
9131 static const char *list
[] = {
9132 /* free form text */
9133 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9134 /* options based on user level */
9135 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9136 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
9137 /* multiple choice options */
9138 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
9139 /* binary options */
9140 "DynLimit", "NoDelete", "BanTimeout",
9144 strlist
= alloc_string_list(ArrayLength(list
)-1);
9145 for(ii
=0; list
[ii
]; ii
++)
9146 string_list_append(strlist
, strdup(list
[ii
]));
9148 chanserv_conf
.set_shows
= strlist
;
9149 /* We don't look things up now, in case the list refers to options
9150 * defined by modules initialized after this point. Just mark the
9151 * function list as invalid, so it will be initialized.
9153 set_shows_list
.used
= 0;
9155 free_string_list(chanserv_conf
.eightball
);
9156 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
9159 strlist
= string_list_copy(strlist
);
9163 strlist
= alloc_string_list(4);
9164 string_list_append(strlist
, strdup("Yes."));
9165 string_list_append(strlist
, strdup("No."));
9166 string_list_append(strlist
, strdup("Maybe so."));
9168 chanserv_conf
.eightball
= strlist
;
9170 free_string_list(chanserv_conf
.old_ban_names
);
9171 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
9173 strlist
= string_list_copy(strlist
);
9175 strlist
= alloc_string_list(2);
9176 chanserv_conf
.old_ban_names
= strlist
;
9177 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
9178 off_channel
= str
? atoi(str
) : 0;
9182 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
9185 struct note_type
*ntype
;
9188 if(!(obj
= GET_RECORD_OBJECT(rd
)))
9190 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
9193 if(!(ntype
= chanserv_create_note_type(key
)))
9195 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
9199 /* Figure out set access */
9200 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
9202 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9203 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
9205 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
9207 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
9208 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
9210 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
9212 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
9216 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
9217 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9218 ntype
->set_access
.min_opserv
= 0;
9221 /* Figure out visibility */
9222 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
9223 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9224 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
9225 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9226 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
9227 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
9228 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
9229 ntype
->visible_type
= NOTE_VIS_ALL
;
9231 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9233 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
9234 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
9238 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9240 struct handle_info
*handle
;
9241 struct userData
*uData
;
9242 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
9244 unsigned short access_level
, lastaccess
= 0;
9246 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9248 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
9252 access_level
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
9253 if(access_level
> UL_OWNER
)
9255 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
9259 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
9260 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
9261 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
9262 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
9263 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9264 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
9265 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
9266 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
9267 lastaccess
= lstacc
? atoi(lstacc
) : 0;
9269 handle
= get_handle_info(key
);
9272 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
9276 uData
= add_channel_user(chan
, handle
, access_level
, last_seen
, inf
, 0);
9277 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
9278 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
9280 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
9281 if (uData
->accessexpiry
> 0)
9282 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
9284 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
9285 if (uData
->clvlexpiry
> 0)
9286 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
9288 uData
->lastaccess
= lastaccess
;
9290 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
9292 if(uData
->expires
> now
)
9293 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
9295 uData
->flags
&= ~USER_SUSPENDED
;
9298 /* Upgrade: set autoop to the inverse of noautoop */
9299 if(chanserv_read_version
< 2)
9301 /* if noautoop is true, set autoop false, and vice versa */
9302 if(uData
->flags
& USER_NOAUTO_OP
)
9303 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
9305 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
9306 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
);
9312 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9314 struct banData
*bData
;
9315 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
9316 time_t set_time
, triggered_time
, expires_time
;
9318 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9320 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
9324 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
9325 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
9326 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
9327 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9328 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
9329 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
9330 if (!reason
|| !owner
)
9333 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
9334 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
9336 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
9338 expires_time
= set_time
+ atoi(s_duration
);
9342 if(!reason
|| (expires_time
&& (expires_time
< now
)))
9345 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
9348 static struct suspended
*
9349 chanserv_read_suspended(dict_t obj
)
9351 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
9355 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
9356 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9357 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
9358 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9359 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9360 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9361 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
9362 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
9363 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9364 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
9368 static struct giveownership
*
9369 chanserv_read_giveownership(dict_t obj
)
9371 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
9375 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
9376 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
9378 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
9380 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
9381 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
9383 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
9384 giveownership
->reason
= str
? strdup(str
) : NULL
;
9385 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9386 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9388 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9389 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
9390 return giveownership
;
9394 chanserv_channel_read(const char *key
, struct record_data
*hir
)
9396 struct suspended
*suspended
;
9397 struct giveownership
*giveownership
;
9398 struct mod_chanmode
*modes
;
9399 struct chanNode
*cNode
;
9400 struct chanData
*cData
;
9401 struct dict
*channel
, *obj
;
9402 char *str
, *argv
[10];
9406 channel
= hir
->d
.object
;
9408 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
9411 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
9414 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
9417 cData
= register_channel(cNode
, str
);
9420 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
9424 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
9426 enum levelOption lvlOpt
;
9427 enum charOption chOpt
;
9429 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
9430 cData
->flags
= atoi(str
);
9432 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9434 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
9436 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
9437 else if(levelOptions
[lvlOpt
].old_flag
)
9439 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9440 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
9442 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
9446 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9448 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
9450 cData
->chOpts
[chOpt
] = str
[0];
9453 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
9455 enum levelOption lvlOpt
;
9456 enum charOption chOpt
;
9459 cData
->flags
= base64toint(str
, 5);
9460 count
= strlen(str
+= 5);
9461 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9464 if(levelOptions
[lvlOpt
].old_flag
)
9466 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9467 lvl
= levelOptions
[lvlOpt
].flag_value
;
9469 lvl
= levelOptions
[lvlOpt
].default_value
;
9471 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
9473 case 'c': lvl
= UL_COOWNER
; break;
9474 case 'm': lvl
= UL_MANAGER
; break;
9475 case 'n': lvl
= UL_OWNER
+1; break;
9476 case 'o': lvl
= UL_OP
; break;
9477 case 'p': lvl
= UL_PEON
; break;
9478 case 'h': lvl
= UL_HALFOP
; break;
9479 case 'w': lvl
= UL_OWNER
; break;
9480 default: lvl
= 0; break;
9482 cData
->lvlOpts
[lvlOpt
] = lvl
;
9484 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9485 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
9488 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
9490 suspended
= chanserv_read_suspended(obj
);
9491 cData
->suspended
= suspended
;
9492 suspended
->cData
= cData
;
9493 /* We could use suspended->expires and suspended->revoked to
9494 * set the CHANNEL_SUSPENDED flag, but we don't. */
9496 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
9498 suspended
= calloc(1, sizeof(*suspended
));
9499 suspended
->issued
= 0;
9500 suspended
->revoked
= 0;
9501 suspended
->suspender
= strdup(str
);
9502 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
9503 suspended
->expires
= str
? atoi(str
) : 0;
9504 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
9505 suspended
->reason
= strdup(str
? str
: "No reason");
9506 suspended
->previous
= NULL
;
9507 cData
->suspended
= suspended
;
9508 suspended
->cData
= cData
;
9512 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9513 suspended
= NULL
; /* to squelch a warning */
9516 if(IsSuspended(cData
)) {
9517 if(suspended
->expires
> now
)
9518 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
9519 else if(suspended
->expires
)
9520 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9523 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
9525 giveownership
= chanserv_read_giveownership(obj
);
9526 cData
->giveownership
= giveownership
;
9529 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
9530 struct mod_chanmode change
;
9531 mod_chanmode_init(&change
);
9533 change
.args
[0].mode
= MODE_CHANOP
;
9534 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
9535 mod_chanmode_announce(chanserv
, cNode
, &change
);
9538 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
9539 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9540 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
9541 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9542 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
9543 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9544 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
9545 cData
->max
= str
? atoi(str
) : 0;
9546 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
9547 cData
->greeting
= str
? strdup(str
) : NULL
;
9548 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
9549 cData
->user_greeting
= str
? strdup(str
) : NULL
;
9550 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
9551 cData
->topic_mask
= str
? strdup(str
) : NULL
;
9552 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
9553 cData
->topic
= str
? strdup(str
) : NULL
;
9555 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
9556 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
9558 if(!IsSuspended(cData
)
9559 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
9560 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
9561 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
9562 cData
->modes
= *modes
;
9564 cData
->modes
.modes_set
|= MODE_REGISTERED
;
9565 if(cData
->modes
.argc
> 1)
9566 cData
->modes
.argc
= 1;
9567 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
9568 mod_chanmode_free(modes
);
9571 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
9572 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9573 user_read_helper(iter_key(it
), iter_data(it
), cData
);
9575 if(!cData
->users
&& !IsProtected(cData
))
9577 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
9578 unregister_channel(cData
, "has empty user list.");
9582 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
9583 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9584 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
9586 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
9587 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9589 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
9590 struct record_data
*rd
= iter_data(it
);
9591 const char *note
, *setter
;
9593 if(rd
->type
!= RECDB_OBJECT
)
9595 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9599 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9601 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9603 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9607 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9608 if(!setter
) setter
= "<unknown>";
9609 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9617 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9619 const char *setter
, *reason
, *str
;
9620 struct do_not_register
*dnr
;
9623 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9626 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9629 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9632 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9635 str
= database_get_data(hir
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9636 expiry
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9637 if(expiry
&& expiry
<= now
)
9639 dnr
= chanserv_add_dnr(key
, setter
, expiry
, reason
);
9642 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9644 dnr
->set
= atoi(str
);
9650 chanserv_version_read(struct dict
*section
)
9654 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9656 chanserv_read_version
= atoi(str
);
9657 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9661 chanserv_saxdb_read(struct dict
*database
)
9663 struct dict
*section
;
9666 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9667 chanserv_version_read(section
);
9669 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9670 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9671 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9673 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9674 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9675 chanserv_channel_read(iter_key(it
), iter_data(it
));
9677 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9678 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9679 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9685 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9687 int high_present
= 0;
9688 saxdb_start_record(ctx
, KEY_USERS
, 1);
9689 for(; uData
; uData
= uData
->next
)
9691 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9693 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9694 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9695 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9696 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9697 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9698 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9700 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9702 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9704 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9705 saxdb_end_record(ctx
);
9707 saxdb_end_record(ctx
);
9708 return high_present
;
9712 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9716 saxdb_start_record(ctx
, KEY_BANS
, 1);
9717 for(; bData
; bData
= bData
->next
)
9719 saxdb_start_record(ctx
, bData
->mask
, 0);
9720 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9721 if(bData
->triggered
)
9722 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9724 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9726 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9728 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9729 saxdb_end_record(ctx
);
9731 saxdb_end_record(ctx
);
9735 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9737 saxdb_start_record(ctx
, name
, 0);
9738 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9739 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9741 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9743 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9745 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9747 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9748 saxdb_end_record(ctx
);
9752 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9754 saxdb_start_record(ctx
, name
, 0);
9755 if(giveownership
->staff_issuer
)
9756 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9757 if(giveownership
->old_owner
)
9758 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9759 if(giveownership
->target
)
9760 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9761 if(giveownership
->target_access
)
9762 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9763 if(giveownership
->reason
)
9764 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9765 if(giveownership
->issued
)
9766 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9767 if(giveownership
->previous
)
9768 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9769 saxdb_end_record(ctx
);
9773 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9777 enum levelOption lvlOpt
;
9778 enum charOption chOpt
;
9780 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9782 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9783 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9785 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9786 if(channel
->registrar
)
9787 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9788 if(channel
->greeting
)
9789 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9790 if(channel
->user_greeting
)
9791 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9792 if(channel
->topic_mask
)
9793 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9794 if(channel
->suspended
)
9795 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9796 if(channel
->giveownership
)
9797 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9799 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9800 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9801 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9802 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9803 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9805 buf
[0] = channel
->chOpts
[chOpt
];
9807 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9809 saxdb_end_record(ctx
);
9811 if (channel
->maxsetinfo
)
9812 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9814 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9816 mod_chanmode_format(&channel
->modes
, buf
);
9817 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9820 high_present
= chanserv_write_users(ctx
, channel
->users
);
9821 chanserv_write_bans(ctx
, channel
->bans
);
9823 if(dict_size(channel
->notes
))
9827 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9828 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9830 struct note
*note
= iter_data(it
);
9831 saxdb_start_record(ctx
, iter_key(it
), 0);
9832 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9833 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9834 saxdb_end_record(ctx
);
9836 saxdb_end_record(ctx
);
9839 if(channel
->ownerTransfer
)
9840 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9841 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9842 saxdb_end_record(ctx
);
9846 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9850 saxdb_start_record(ctx
, ntype
->name
, 0);
9851 switch(ntype
->set_access_type
)
9853 case NOTE_SET_CHANNEL_ACCESS
:
9854 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9856 case NOTE_SET_CHANNEL_SETTER
:
9857 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9859 case NOTE_SET_PRIVILEGED
: default:
9860 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9863 switch(ntype
->visible_type
)
9865 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9866 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9867 case NOTE_VIS_PRIVILEGED
:
9868 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9870 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9871 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9872 saxdb_end_record(ctx
);
9876 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9878 struct do_not_register
*dnr
;
9879 dict_iterator_t it
, next
;
9881 for(it
= dict_first(dnrs
); it
; it
= next
)
9883 next
= iter_next(it
);
9884 dnr
= iter_data(it
);
9885 if(dnr
->expires
&& dnr
->expires
<= now
)
9887 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9889 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9892 dict_remove(dnrs
, iter_key(it
));
9893 saxdb_write_int(ctx
, KEY_EXPIRES
, dnr
->expires
);
9895 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9896 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9897 saxdb_end_record(ctx
);
9902 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9905 struct chanData
*channel
;
9907 /* Version Control*/
9908 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9909 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9910 saxdb_end_record(ctx
);
9913 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9914 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9915 chanserv_write_note_type(ctx
, iter_data(it
));
9916 saxdb_end_record(ctx
);
9919 saxdb_start_record(ctx
, KEY_DNR
, 1);
9920 write_dnrs_helper(ctx
, handle_dnrs
);
9921 write_dnrs_helper(ctx
, plain_dnrs
);
9922 write_dnrs_helper(ctx
, mask_dnrs
);
9923 saxdb_end_record(ctx
);
9926 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9927 for(channel
= channelList
; channel
; channel
= channel
->next
)
9928 chanserv_write_channel(ctx
, channel
);
9929 saxdb_end_record(ctx
);
9935 chanserv_db_cleanup(UNUSED_ARG(void *extra
)) {
9937 unreg_part_func(handle_part
, NULL
);
9939 unregister_channel(channelList
, "terminating.");
9940 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9941 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9942 free(chanserv_conf
.support_channels
.list
);
9943 dict_delete(handle_dnrs
);
9944 dict_delete(plain_dnrs
);
9945 dict_delete(mask_dnrs
);
9946 dict_delete(note_types
);
9947 free_string_list(chanserv_conf
.eightball
);
9948 free_string_list(chanserv_conf
.old_ban_names
);
9949 free_string_list(chanserv_conf
.wheel
);
9950 free_string_list(chanserv_conf
.set_shows
);
9951 free(set_shows_list
.list
);
9952 free(uset_shows_list
.list
);
9955 struct userData
*helper
= helperList
;
9956 helperList
= helperList
->next
;
9961 #if defined(GCC_VARMACROS)
9962 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
9963 #elif defined(C99_VARMACROS)
9964 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
9966 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9967 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9970 init_chanserv(const char *nick
)
9972 struct chanNode
*chan
;
9975 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9976 conf_register_reload(chanserv_conf_read
);
9979 reg_server_link_func(handle_server_link
, NULL
);
9980 reg_new_channel_func(handle_new_channel
, NULL
);
9981 reg_join_func(handle_join
, NULL
);
9982 reg_part_func(handle_part
, NULL
);
9983 reg_kick_func(handle_kick
, NULL
);
9984 reg_topic_func(handle_topic
, NULL
);
9985 reg_mode_change_func(handle_mode
, NULL
);
9986 reg_nick_change_func(handle_nick_change
, NULL
);
9987 reg_auth_func(handle_auth
, NULL
);
9990 reg_handle_rename_func(handle_rename
, NULL
);
9991 reg_unreg_func(handle_unreg
, NULL
);
9993 handle_dnrs
= dict_new();
9994 dict_set_free_data(handle_dnrs
, free
);
9995 plain_dnrs
= dict_new();
9996 dict_set_free_data(plain_dnrs
, free
);
9997 mask_dnrs
= dict_new();
9998 dict_set_free_data(mask_dnrs
, free
);
10000 reg_svccmd_unbind_func(handle_svccmd_unbind
, NULL
);
10001 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
10002 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+channel", NULL
);
10003 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
10004 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
10005 DEFINE_COMMAND(dnrsearch
, 3, 0, "template", "noregister", NULL
);
10006 modcmd_register(chanserv_module
, "dnrsearch print", NULL
, 0, 0, NULL
);
10007 modcmd_register(chanserv_module
, "dnrsearch remove", NULL
, 0, 0, NULL
);
10008 modcmd_register(chanserv_module
, "dnrsearch count", NULL
, 0, 0, NULL
);
10009 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
10010 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
|MODCMD_IGNORE_CSUSPEND
, "flags", "+helping", NULL
);
10011 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
|MODCMD_IGNORE_CSUSPEND
, "flags", "+helping", NULL
);
10012 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
10013 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
10015 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
10017 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
10018 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
10020 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10021 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10022 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10023 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10024 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10026 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
10027 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
10028 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
10029 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10030 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10031 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10032 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10034 DEFINE_COMMAND(levels
, 1, 0, NULL
);
10036 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10037 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
10038 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10039 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
10041 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10042 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
10043 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10044 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10045 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10046 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10047 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10048 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10049 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10050 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10052 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10053 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10054 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10055 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
10056 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
10057 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
10058 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
10059 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
10060 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
10061 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
10062 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
10063 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
10064 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10065 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10067 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10068 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10069 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10070 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10072 /* if you change dellamer access, see also places
10073 * like unbanme which have manager hardcoded. */
10074 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10075 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
10077 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
10079 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
10081 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10082 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10083 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10084 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10085 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10086 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10087 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10088 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10089 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10090 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10091 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10092 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10094 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
10095 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
10097 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
10098 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
10099 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
10100 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
10102 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10103 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10104 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
10105 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
10106 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
10108 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10109 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10110 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10111 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10112 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10113 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10114 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10115 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10116 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10117 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10118 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
10120 /* Channel options */
10121 DEFINE_CHANNEL_OPTION(defaulttopic
);
10122 DEFINE_CHANNEL_OPTION(topicmask
);
10123 DEFINE_CHANNEL_OPTION(greeting
);
10124 DEFINE_CHANNEL_OPTION(usergreeting
);
10125 DEFINE_CHANNEL_OPTION(modes
);
10126 DEFINE_CHANNEL_OPTION(enfops
);
10127 DEFINE_CHANNEL_OPTION(enfhalfops
);
10128 DEFINE_CHANNEL_OPTION(automode
);
10129 DEFINE_CHANNEL_OPTION(protect
);
10130 DEFINE_CHANNEL_OPTION(enfmodes
);
10131 DEFINE_CHANNEL_OPTION(enftopic
);
10132 DEFINE_CHANNEL_OPTION(pubcmd
);
10133 DEFINE_CHANNEL_OPTION(userinfo
);
10134 DEFINE_CHANNEL_OPTION(dynlimit
);
10135 DEFINE_CHANNEL_OPTION(topicsnarf
);
10136 DEFINE_CHANNEL_OPTION(nodelete
);
10137 DEFINE_CHANNEL_OPTION(toys
);
10138 DEFINE_CHANNEL_OPTION(setters
);
10139 DEFINE_CHANNEL_OPTION(topicrefresh
);
10140 DEFINE_CHANNEL_OPTION(resync
);
10141 DEFINE_CHANNEL_OPTION(ctcpreaction
);
10142 DEFINE_CHANNEL_OPTION(bantimeout
);
10143 DEFINE_CHANNEL_OPTION(inviteme
);
10144 DEFINE_CHANNEL_OPTION(unreviewed
);
10145 modcmd_register(chanserv_module
, "set unreviewed on", NULL
, 0, 0, "flags", "+helping", NULL
);
10146 modcmd_register(chanserv_module
, "set unreviewed off", NULL
, 0, 0, "flags", "+oper", NULL
);
10147 DEFINE_CHANNEL_OPTION(maxsetinfo
);
10148 if(off_channel
> 1)
10149 DEFINE_CHANNEL_OPTION(offchannel
);
10150 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
10152 /* Alias set topic to set defaulttopic for compatibility. */
10153 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
10156 DEFINE_USER_OPTION(autoinvite
);
10157 DEFINE_USER_OPTION(autojoin
);
10158 DEFINE_USER_OPTION(info
);
10159 DEFINE_USER_OPTION(autoop
);
10161 /* Alias uset autovoice to uset autoop. */
10162 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
10164 note_types
= dict_new();
10165 dict_set_free_data(note_types
, chanserv_deref_note_type
);
10168 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
10169 chanserv
= AddLocalUser(nick
, nick
, NULL
, "Channel Services", modes
);
10170 service_register(chanserv
)->trigger
= '!';
10171 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
, NULL
);
10174 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
10176 if(chanserv_conf
.channel_expire_frequency
)
10177 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
10179 if(chanserv_conf
.dnr_expire_frequency
)
10180 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
10182 if(chanserv_conf
.ban_timeout_frequency
)
10183 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
10185 if(chanserv_conf
.refresh_period
)
10187 time_t next_refresh
;
10188 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
10189 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
10190 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
10193 if (autojoin_channels
&& chanserv
) {
10194 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
10195 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
10196 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
10200 reg_exit_func(chanserv_db_cleanup
, NULL
);
10201 message_register_table(msgtab
);