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_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
261 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
262 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
263 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
264 { "CSMSG_BAD_BAN", "The given ban $b%s$b is invalid." },
266 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
268 /* Channel management */
269 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
270 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
271 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
273 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
274 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
275 { "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" },
276 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
277 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
278 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
279 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
281 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
282 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
283 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
284 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
285 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
286 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
287 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
288 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
289 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
290 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
291 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
292 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
293 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
294 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
295 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
296 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
297 { "CSMSG_SET_MODES", "$bModes $b %s" },
298 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
299 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
300 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
301 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
302 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
303 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
304 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
305 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
306 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
307 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
308 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
309 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
310 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
311 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
312 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
313 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
314 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
315 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
316 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
317 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
318 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
320 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
321 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
322 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
323 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
324 { "CSMSG_USET_INFO", "$bInfo $b %s" },
326 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
327 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
328 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
329 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
330 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
331 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
332 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
333 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
334 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
335 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
336 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
338 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
339 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
340 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
341 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
342 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
343 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
344 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
346 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
347 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
348 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
349 { "CSMSG_PROTECT_NONE", "No users will be protected." },
350 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
351 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
352 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
354 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
355 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
356 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
357 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
358 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
360 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
361 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
362 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
363 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
364 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
366 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
367 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
368 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
369 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
370 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
372 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
373 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
374 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
375 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
376 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
377 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
379 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
380 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
381 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
382 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
383 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
384 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
385 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
386 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
387 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
389 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
390 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
391 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
393 /* Channel userlist */
394 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
395 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
396 /* uncomment if needed to adujust styles (and change code below)
397 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
398 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
399 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
400 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
401 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
402 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
404 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
405 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
406 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
408 /* Channel note list */
409 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
410 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
411 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
412 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
413 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
414 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
415 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
416 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
417 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
418 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
419 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
420 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
421 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
422 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
423 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
425 /* Channel [un]suspension */
426 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
427 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
428 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
429 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
430 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
431 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
432 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
434 /* Access information */
435 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
436 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
437 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
438 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
439 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
440 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
441 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
442 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
443 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
444 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
445 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
446 { "CSMSG_UC_H_TITLE", "network helper" },
447 { "CSMSG_LC_H_TITLE", "support helper" },
448 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
450 /* Seen information */
451 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
452 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
453 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
454 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
456 /* Names information */
457 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
458 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
460 /* Channel information */
461 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
462 { "CSMSG_BAR", "----------------------------------------"},
463 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
464 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
465 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
466 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
467 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
468 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
469 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
470 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
471 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
472 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
473 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
474 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
475 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
476 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
477 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
478 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
479 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
480 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
481 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
482 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
483 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
484 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
485 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
486 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
487 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
488 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
490 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
491 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
492 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
493 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
494 { "CSMSG_PEEK_OPS", "$bOps:$b" },
495 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
496 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
498 /* Network information */
499 { "CSMSG_NETWORK_INFO", "Network Information:" },
500 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
501 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
502 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
503 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
504 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
505 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
506 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
507 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
510 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
511 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
512 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
514 /* Channel searches */
515 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
516 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
517 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
518 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
520 /* Channel configuration */
521 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
522 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
523 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
524 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
525 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
528 { "CSMSG_USER_OPTIONS", "User Options:" },
529 // { "CSMSG_USER_PROTECTED", "That user is protected." },
532 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
533 { "CSMSG_PING_RESPONSE", "Pong!" },
534 { "CSMSG_WUT_RESPONSE", "wut" },
535 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
536 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
537 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
538 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
539 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
540 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
541 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
542 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
543 { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
544 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
545 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
546 { "CSMSG_ROULETTE_CLICK", "Click" } ,
548 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
549 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
550 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
552 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
553 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
554 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
555 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
556 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
557 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
558 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
559 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
560 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
561 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
562 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
563 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
564 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
565 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
568 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
569 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
570 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
571 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
575 /* eject_user and unban_user flags */
576 #define ACTION_KICK 0x0001
577 #define ACTION_BAN 0x0002
578 #define ACTION_ADD_LAMER 0x0004
579 #define ACTION_ADD_TIMED_LAMER 0x0008
580 #define ACTION_UNBAN 0x0010
581 #define ACTION_DEL_LAMER 0x0020
583 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
584 #define MODELEN 40 + KEYLEN
588 #define CSFUNC_ARGS user, channel, argc, argv, cmd
590 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
591 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
592 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
593 reply("MSG_MISSING_PARAMS", argv[0]); \
597 DECLARE_LIST(dnrList
, struct do_not_register
*);
598 DEFINE_LIST(dnrList
, struct do_not_register
*)
600 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
602 struct userNode
*chanserv
;
605 extern struct string_list
*autojoin_channels
;
606 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
607 static struct log_type
*CS_LOG
;
608 struct adduserPending
* adduser_pendings
= NULL
;
609 unsigned int adduser_pendings_count
= 0;
610 unsigned long god_timeout
;
614 struct channelList support_channels
;
615 struct mod_chanmode default_modes
;
617 unsigned long db_backup_frequency
;
618 unsigned long channel_expire_frequency
;
619 unsigned long ban_timeout_frequency
;
620 unsigned long dnr_expire_frequency
;
623 unsigned int adjust_delay
;
624 long channel_expire_delay
;
625 unsigned int nodelete_level
;
627 unsigned int adjust_threshold
;
628 int join_flood_threshold
;
630 unsigned int greeting_length
;
631 unsigned int refresh_period
;
632 unsigned int giveownership_period
;
634 unsigned int max_owned
;
635 unsigned int max_chan_users
;
636 unsigned int max_chan_bans
; /* lamers */
637 unsigned int max_userinfo_length
;
638 unsigned int valid_channel_regex_set
: 1;
640 regex_t valid_channel_regex
;
642 struct string_list
*set_shows
;
643 struct string_list
*eightball
;
644 struct string_list
*old_ban_names
;
645 struct string_list
*wheel
;
647 const char *ctcp_short_ban_duration
;
648 const char *ctcp_long_ban_duration
;
650 const char *irc_operator_epithet
;
651 const char *network_helper_epithet
;
652 const char *support_helper_epithet
;
657 struct userNode
*user
;
658 struct userNode
*bot
;
659 struct chanNode
*channel
;
661 unsigned short lowest
;
662 unsigned short highest
;
663 struct userData
**users
;
664 struct helpfile_table table
;
667 enum note_access_type
669 NOTE_SET_CHANNEL_ACCESS
,
670 NOTE_SET_CHANNEL_SETTER
,
674 enum note_visible_type
677 NOTE_VIS_CHANNEL_USERS
,
681 struct io_fd
*socket_io_fd
;
682 extern struct cManagerNode cManager
;
686 enum note_access_type set_access_type
;
688 unsigned int min_opserv
;
689 unsigned short min_ulevel
;
691 enum note_visible_type visible_type
;
692 unsigned int max_length
;
699 struct note_type
*type
;
700 char setter
[NICKSERV_HANDLE_LEN
+1];
704 static unsigned int registered_channels
;
705 static unsigned int banCount
;
707 static const struct {
710 unsigned short level
;
712 } accessLevels
[] = { /* MUST be orderd less to most! */
713 { "pal", "Pal", UL_PEON
, '+' },
714 { "peon", "Peon", UL_PEON
, '+' },
715 { "halfop", "HalfOp", UL_HALFOP
, '%' },
716 { "op", "Op", UL_OP
, '@' },
717 { "manager", "Manager", UL_MANAGER
, '%' },
718 { "coowner", "Coowner", UL_COOWNER
, '*' },
719 { "owner", "Owner", UL_OWNER
, '!' },
720 { "helper", "BUG:", UL_HELPER
, 'X' }
723 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
724 static const struct {
727 unsigned short default_value
;
728 unsigned int old_idx
;
729 unsigned int old_flag
;
730 unsigned short flag_value
;
732 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
733 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
734 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
735 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
736 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
737 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
738 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
739 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
740 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
743 struct charOptionValues
{
746 } automodeValues
[] = {
747 { 'n', "CSMSG_AUTOMODE_NONE" },
748 { 'y', "CSMSG_AUTOMODE_NORMAL" },
749 { 'v', "CSMSG_AUTOMODE_VOICE" },
750 { 'h', "CSMSG_AUTOMODE_HOP" },
751 { 'o', "CSMSG_AUTOMODE_OP" },
752 { 'm', "CSMSG_AUTOMODE_MUTE" },
753 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
754 }, protectValues
[] = {
755 { 'a', "CSMSG_PROTECT_ALL" },
756 { 'e', "CSMSG_PROTECT_EQUAL" },
757 { 'l', "CSMSG_PROTECT_LOWER" },
758 { 'n', "CSMSG_PROTECT_NONE" }
760 { 'd', "CSMSG_TOYS_DISABLED" },
761 { 'n', "CSMSG_TOYS_PRIVATE" },
762 { 'p', "CSMSG_TOYS_PUBLIC" }
763 }, topicRefreshValues
[] = {
764 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
765 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
766 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
767 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
768 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
769 }, ctcpReactionValues
[] = {
770 { 'n', "CSMSG_CTCPREACTION_NONE" },
771 { 'k', "CSMSG_CTCPREACTION_KICK" },
772 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
773 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
774 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
775 }, banTimeoutValues
[] = {
776 { '0', "CSMSG_BANTIMEOUT_NONE" },
777 { '1', "CSMSG_BANTIMEOUT_10M" },
778 { '2', "CSMSG_BANTIMEOUT_2H" },
779 { '3', "CSMSG_BANTIMEOUT_4H" },
780 { '4', "CSMSG_BANTIMEOUT_1D" },
781 { '5', "CSMSG_BANTIMEOUT_1W" }
784 { 'n', "CSMSG_RESYNC_NEVER" },
785 { '1', "CSMSG_RESYNC_3_HOURS" },
786 { '2', "CSMSG_RESYNC_6_HOURS" },
787 { '3', "CSMSG_RESYNC_12_HOURS" },
788 { '4', "CSMSG_RESYNC_24_HOURS" }
791 static const struct {
795 unsigned int old_idx
;
797 struct charOptionValues
*values
;
799 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
800 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
801 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
802 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
803 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
804 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
805 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
808 struct userData
*helperList
;
809 struct chanData
*channelList
;
810 static struct module *chanserv_module
;
811 static unsigned int userCount
;
812 unsigned int chanserv_read_version
= 0; /* db version control */
814 #define CHANSERV_DB_VERSION 2
816 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
817 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
819 void sputsock(const char *text
, ...) PRINTF_LIKE(1, 2);
822 sputsock(const char *text
, ...)
828 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
830 va_start(arg_list
, text
);
831 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
833 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
835 log_replay(MAIN_LOG
, true, buffer
);
836 buffer
[pos
++] = '\n';
838 ioset_write(socket_io_fd
, buffer
, pos
);
842 user_level_from_name(const char *name
, unsigned short clamp_level
)
844 unsigned int level
= 0, ii
;
846 level
= strtoul(name
, NULL
, 10);
847 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
848 if(!irccasecmp(name
, accessLevels
[ii
].name
))
849 level
= accessLevels
[ii
].level
;
850 if(level
> clamp_level
)
856 user_level_name_from_level(int level
)
864 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
865 if(level
>= accessLevels
[ii
].level
)
866 highest
= accessLevels
[ii
].title
;
872 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
875 *minl
= strtoul(arg
, &sep
, 10);
883 *maxl
= strtoul(sep
+1, &sep
, 10);
891 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
893 struct userData
*uData
, **head
;
895 if(!channel
|| !handle
)
898 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
899 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
901 for(uData
= helperList
;
902 uData
&& uData
->handle
!= handle
;
903 uData
= uData
->next
);
907 uData
= calloc(1, sizeof(struct userData
));
908 uData
->handle
= handle
;
910 uData
->access
= UL_HELPER
;
916 uData
->next
= helperList
;
918 helperList
->prev
= uData
;
926 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
927 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
930 head
= &(channel
->users
);
933 if(uData
&& (uData
!= *head
))
935 /* Shuffle the user to the head of whatever list he was in. */
937 uData
->next
->prev
= uData
->prev
;
939 uData
->prev
->next
= uData
->next
;
945 (**head
).prev
= uData
;
952 /* Returns non-zero if user has at least the minimum access.
953 * exempt_owner is set when handling !set, so the owner can set things
956 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
958 struct userData
*uData
;
959 struct chanData
*cData
= channel
->channel_info
;
960 unsigned short minimum
= cData
->lvlOpts
[opt
];
963 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
966 if(minimum
<= uData
->access
)
968 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
973 /* Scan for other users authenticated to the same handle
974 still in the channel. If so, keep them listed as present.
976 user is optional, if not null, it skips checking that userNode
977 (for the handle_part function) */
979 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
983 if(IsSuspended(uData
->channel
)
984 || IsUserSuspended(uData
)
985 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
997 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
))
999 unsigned int eflags
, argc
;
1001 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
1003 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
1004 if(!channel
->channel_info
1005 || IsSuspended(channel
->channel_info
)
1007 || !ircncasecmp(text
, "ACTION ", 7))
1009 /* We dont punish people we know -Rubin
1010 * * Figure out the minimum level needed to CTCP the channel *
1012 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
1015 /* If they are a user of the channel, they are exempt */
1016 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
1018 /* We need to enforce against them; do so. */
1020 argv
[0] = (char*)text
;
1021 argv
[1] = user
->nick
;
1023 if(GetUserMode(channel
, user
))
1024 eflags
|= ACTION_KICK
;
1025 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
1026 default: case 'n': return;
1028 eflags
|= ACTION_KICK
;
1031 eflags
|= ACTION_BAN
;
1034 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1035 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
1038 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1039 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
1042 argv
[argc
++] = bad_ctcp_reason
;
1043 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
1047 chanserv_create_note_type(const char *name
)
1049 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
1050 strcpy(ntype
->name
, name
);
1052 dict_insert(note_types
, ntype
->name
, ntype
);
1057 chanserv_deref_note_type(void *data
)
1059 struct note_type
*ntype
= data
;
1061 if(--ntype
->refs
> 0)
1067 chanserv_flush_note_type(struct note_type
*ntype
)
1069 struct chanData
*cData
;
1070 for(cData
= channelList
; cData
; cData
= cData
->next
)
1071 dict_remove(cData
->notes
, ntype
->name
);
1075 chanserv_truncate_notes(struct note_type
*ntype
)
1077 struct chanData
*cData
;
1079 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1081 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1082 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1085 if(strlen(note
->note
) <= ntype
->max_length
)
1087 dict_remove2(cData
->notes
, ntype
->name
, 1);
1088 note
= realloc(note
, size
);
1089 note
->note
[ntype
->max_length
] = 0;
1090 dict_insert(cData
->notes
, ntype
->name
, note
);
1094 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1096 static struct note
*
1097 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1100 unsigned int len
= strlen(text
);
1102 if(len
> type
->max_length
) len
= type
->max_length
;
1103 note
= calloc(1, sizeof(*note
) + len
);
1105 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1106 memcpy(note
->note
, text
, len
);
1107 note
->note
[len
] = 0;
1108 dict_insert(channel
->notes
, type
->name
, note
);
1114 chanserv_free_note(void *data
)
1116 struct note
*note
= data
;
1118 chanserv_deref_note_type(note
->type
);
1119 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1123 static MODCMD_FUNC(cmd_createnote
) {
1124 struct note_type
*ntype
;
1125 unsigned int arg
= 1, existed
= 0, max_length
;
1127 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1130 ntype
= chanserv_create_note_type(argv
[arg
]);
1131 if(!irccasecmp(argv
[++arg
], "privileged"))
1134 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1135 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1137 else if(!irccasecmp(argv
[arg
], "channel"))
1139 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1142 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1145 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1146 ntype
->set_access
.min_ulevel
= ulvl
;
1148 else if(!irccasecmp(argv
[arg
], "setter"))
1150 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1154 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1158 if(!irccasecmp(argv
[++arg
], "privileged"))
1159 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1160 else if(!irccasecmp(argv
[arg
], "channel_users"))
1161 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1162 else if(!irccasecmp(argv
[arg
], "all"))
1163 ntype
->visible_type
= NOTE_VIS_ALL
;
1165 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1169 if((arg
+1) >= argc
) {
1170 reply("MSG_MISSING_PARAMS", argv
[0]);
1173 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1174 if(max_length
< 20 || max_length
> 450)
1176 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1179 if(existed
&& (max_length
< ntype
->max_length
))
1181 ntype
->max_length
= max_length
;
1182 chanserv_truncate_notes(ntype
);
1184 ntype
->max_length
= max_length
;
1187 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1189 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1194 dict_remove(note_types
, ntype
->name
);
1198 static MODCMD_FUNC(cmd_removenote
) {
1199 struct note_type
*ntype
;
1202 ntype
= dict_find(note_types
, argv
[1], NULL
);
1203 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1206 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1213 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1216 chanserv_flush_note_type(ntype
);
1218 dict_remove(note_types
, argv
[1]);
1219 reply("CSMSG_NOTE_DELETED", argv
[1]);
1224 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1228 if(orig
->modes_set
& change
->modes_clear
)
1230 if(orig
->modes_clear
& change
->modes_set
)
1232 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1233 && strcmp(orig
->new_key
, change
->new_key
))
1235 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1236 && (orig
->new_limit
!= change
->new_limit
))
1241 static char max_length_text
[MAXLEN
+1][16];
1243 static struct helpfile_expansion
1244 chanserv_expand_variable(const char *variable
)
1246 struct helpfile_expansion exp
;
1248 if(!irccasecmp(variable
, "notes"))
1251 exp
.type
= HF_TABLE
;
1252 exp
.value
.table
.length
= 1;
1253 exp
.value
.table
.width
= 3;
1254 exp
.value
.table
.flags
= 0;
1255 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1256 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1257 exp
.value
.table
.contents
[0][0] = "Note Type";
1258 exp
.value
.table
.contents
[0][1] = "Visibility";
1259 exp
.value
.table
.contents
[0][2] = "Max Length";
1260 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1262 struct note_type
*ntype
= iter_data(it
);
1265 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1266 row
= exp
.value
.table
.length
++;
1267 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1268 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1269 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1270 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1272 if(!max_length_text
[ntype
->max_length
][0])
1273 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1274 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1279 exp
.type
= HF_STRING
;
1280 exp
.value
.str
= NULL
;
1284 static struct chanData
*
1285 register_channel(struct chanNode
*cNode
, char *registrar
)
1287 struct chanData
*channel
;
1288 enum levelOption lvlOpt
;
1289 enum charOption chOpt
;
1291 channel
= calloc(1, sizeof(struct chanData
));
1293 channel
->notes
= dict_new();
1294 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1296 channel
->registrar
= strdup(registrar
);
1297 channel
->registered
= now
;
1298 channel
->visited
= now
;
1299 channel
->limitAdjusted
= now
;
1300 channel
->ownerTransfer
= now
;
1301 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1302 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1303 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1304 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1305 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1307 channel
->prev
= NULL
;
1308 channel
->next
= channelList
;
1311 channelList
->prev
= channel
;
1312 channelList
= channel
;
1313 registered_channels
++;
1315 channel
->channel
= cNode
;
1317 cNode
->channel_info
= channel
;
1322 static struct userData
*
1323 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access_level
, time_t seen
, const char *info
, time_t accessexpiry
)
1325 struct userData
*ud
;
1327 if(access_level
> UL_OWNER
)
1330 ud
= calloc(1, sizeof(*ud
));
1331 ud
->channel
= channel
;
1332 ud
->handle
= handle
;
1334 ud
->access
= access_level
;
1335 ud
->info
= info
? strdup(info
) : NULL
;
1336 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1341 ud
->next
= channel
->users
;
1343 channel
->users
->prev
= ud
;
1344 channel
->users
= ud
;
1346 channel
->userCount
++;
1350 ud
->u_next
= ud
->handle
->channels
;
1352 ud
->u_next
->u_prev
= ud
;
1353 ud
->handle
->channels
= ud
;
1355 ud
->flags
= USER_FLAGS_DEFAULT
;
1359 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1362 chanserv_expire_tempuser(void *data
)
1364 struct userData
*uData
= data
;
1368 handle
= strdup(uData
->handle
->handle
);
1369 if (uData
->accessexpiry
> 0) {
1370 if (uData
->present
) {
1371 struct userNode
*user
, *next_un
= NULL
;
1372 struct handle_info
*hi
;
1374 hi
= get_handle_info(handle
);
1375 for (user
= hi
->users
; user
; user
= next_un
) {
1376 struct mod_chanmode
*change
;
1377 struct modeNode
*mn
;
1378 unsigned int count
= 0;
1380 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1381 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1382 next_un
= user
->next_authed
;
1386 change
= mod_chanmode_alloc(2);
1387 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1388 change
->args
[count
++].u
.member
= mn
;
1391 change
->argc
= count
;
1392 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1394 mod_chanmode_free(change
);
1395 next_un
= user
->next_authed
;
1398 del_channel_user(uData
, 1);
1404 chanserv_expire_tempclvl(void *data
)
1406 struct userData
*uData
= data
;
1410 handle
= strdup(uData
->handle
->handle
);
1411 if (uData
->clvlexpiry
> 0) {
1412 int changemodes
= 0;
1413 unsigned int mode
= 0;
1415 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1417 mode
= MODE_REMOVE
| MODE_CHANOP
;
1418 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1420 mode
= MODE_REMOVE
| MODE_HALFOP
;
1424 if (uData
->present
) {
1425 struct userNode
*user
, *next_un
= NULL
;
1426 struct handle_info
*hi
;
1428 hi
= get_handle_info(handle
);
1429 for (user
= hi
->users
; user
; user
= next_un
) {
1430 struct mod_chanmode
*change
;
1431 struct modeNode
*mn
;
1432 unsigned int count
= 0;
1434 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1435 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1436 next_un
= user
->next_authed
;
1440 if (changemodes
== 0) {
1441 next_un
= user
->next_authed
;
1445 change
= mod_chanmode_alloc(2);
1446 change
->args
[count
].mode
= mode
;
1447 change
->args
[count
++].u
.member
= mn
;
1450 change
->argc
= count
;
1451 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1453 mod_chanmode_free(change
);
1454 next_un
= user
->next_authed
;
1458 uData
->access
= uData
->lastaccess
;
1459 uData
->lastaccess
= 0;
1460 uData
->clvlexpiry
= 0;
1466 del_channel_user(struct userData
*user
, int do_gc
)
1468 struct chanData
*channel
= user
->channel
;
1470 channel
->userCount
--;
1473 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1474 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1477 user
->prev
->next
= user
->next
;
1479 channel
->users
= user
->next
;
1481 user
->next
->prev
= user
->prev
;
1484 user
->u_prev
->u_next
= user
->u_next
;
1486 user
->handle
->channels
= user
->u_next
;
1488 user
->u_next
->u_prev
= user
->u_prev
;
1492 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1493 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1494 unregister_channel(channel
, "lost all users.");
1498 static struct adduserPending
*
1499 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1501 struct adduserPending
*ap
;
1502 ap
= calloc(1,sizeof(struct adduserPending
));
1503 ap
->channel
= channel
;
1506 ap
->created
= time(NULL
);
1508 /* ap->prev defaults to NULL already.. */
1509 ap
->next
= adduser_pendings
;
1510 if(adduser_pendings
)
1511 adduser_pendings
->prev
= ap
;
1512 adduser_pendings
= ap
;
1513 adduser_pendings_count
++;
1518 del_adduser_pending(struct adduserPending
*ap
)
1521 ap
->prev
->next
= ap
->next
;
1523 adduser_pendings
= ap
->next
;
1526 ap
->next
->prev
= ap
->prev
;
1530 static void expire_adduser_pending();
1532 /* find_adduser_pending(channel, user) will find an arbitrary record
1533 * from user, channel, or user and channel.
1534 * if user or channel are NULL, they will match any records.
1536 static struct adduserPending
*
1537 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1539 struct adduserPending
*ap
;
1541 expire_adduser_pending(); /* why not here.. */
1543 if(!channel
&& !user
) /* 2 nulls matches all */
1544 return(adduser_pendings
);
1545 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1547 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1554 /* Remove all pendings for a user or channel
1556 * called in nickserv.c DelUser() and proto-* unregister_channel()
1559 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1561 struct adduserPending
*ap
;
1563 /* So this is a bit wastefull, i hate dealing with linked lists.
1564 * if its a problem we'll rewrite it right */
1565 while((ap
= find_adduser_pending(channel
, user
))) {
1566 del_adduser_pending(ap
);
1570 /* Called from nickserv.c cmd_auth after someone auths */
1572 process_adduser_pending(struct userNode
*user
)
1574 struct adduserPending
*ap
;
1575 if(!user
->handle_info
)
1576 return; /* not associated with an account */
1577 while((ap
= find_adduser_pending(NULL
, user
)))
1579 struct userData
*actee
;
1580 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1582 /* Already on the userlist. do nothing*/
1586 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1587 scan_user_presence(actee
, NULL
);
1589 del_adduser_pending(ap
);
1594 expire_adduser_pending()
1596 struct adduserPending
*ap
, *ap_next
;
1597 ap
= adduser_pendings
;
1600 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1602 ap_next
= ap
->next
; /* save next */
1603 del_adduser_pending(ap
); /* free and relink */
1604 ap
= ap_next
; /* advance */
1611 static void expire_ban(void *data
);
1614 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1617 unsigned int ii
, l1
, l2
;
1622 bd
= malloc(sizeof(struct banData
));
1624 bd
->channel
= channel
;
1626 bd
->triggered
= triggered
;
1627 bd
->expires
= expires
;
1629 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1631 extern const char *hidden_host_suffix
;
1632 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1636 l2
= strlen(old_name
);
1639 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1641 new_mask
= alloca(MAXLEN
);
1642 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1645 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1647 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1648 bd
->reason
= strdup(reason
);
1651 timeq_add(expires
, expire_ban
, bd
);
1654 bd
->next
= channel
->bans
; /* lamers */
1656 channel
->bans
->prev
= bd
;
1658 channel
->banCount
++;
1665 del_channel_ban(struct banData
*ban
)
1667 ban
->channel
->banCount
--;
1671 ban
->prev
->next
= ban
->next
;
1673 ban
->channel
->bans
= ban
->next
;
1676 ban
->next
->prev
= ban
->prev
;
1679 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1688 expire_ban(void *data
) /* lamer.. */
1690 struct banData
*bd
= data
;
1691 if(!IsSuspended(bd
->channel
))
1693 struct banList bans
;
1694 struct mod_chanmode change
;
1696 bans
= bd
->channel
->channel
->banlist
;
1697 mod_chanmode_init(&change
);
1698 for(ii
=0; ii
<bans
.used
; ii
++)
1700 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1703 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1704 change
.args
[0].u
.hostmask
= bd
->mask
;
1705 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1711 del_channel_ban(bd
);
1714 static void chanserv_expire_suspension(void *data
);
1717 unregister_channel(struct chanData
*channel
, const char *reason
)
1719 struct mod_chanmode change
;
1720 char msgbuf
[MAXLEN
];
1722 /* After channel unregistration, the following must be cleaned
1724 - Channel information.
1726 - Channel bans. (lamers)
1727 - Channel suspension data.
1728 - adduser_pending data.
1729 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1735 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1739 mod_chanmode_init(&change
);
1740 change
.modes_clear
|= MODE_REGISTERED
;
1741 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1744 wipe_adduser_pending(channel
->channel
, NULL
);
1746 while(channel
->users
)
1747 del_channel_user(channel
->users
, 0);
1749 while(channel
->bans
)
1750 del_channel_ban(channel
->bans
);
1752 free(channel
->topic
);
1753 free(channel
->registrar
);
1754 free(channel
->greeting
);
1755 free(channel
->user_greeting
);
1756 free(channel
->topic_mask
);
1759 channel
->prev
->next
= channel
->next
;
1761 channelList
= channel
->next
;
1764 channel
->next
->prev
= channel
->prev
;
1766 if(channel
->suspended
)
1768 struct chanNode
*cNode
= channel
->channel
;
1769 struct suspended
*suspended
, *next_suspended
;
1771 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1773 next_suspended
= suspended
->previous
;
1774 free(suspended
->suspender
);
1775 free(suspended
->reason
);
1776 if(suspended
->expires
)
1777 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1782 cNode
->channel_info
= NULL
;
1784 channel
->channel
->channel_info
= NULL
;
1786 dict_delete(channel
->notes
);
1787 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1788 if(!IsSuspended(channel
))
1789 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1790 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1791 UnlockChannel(channel
->channel
);
1793 registered_channels
--;
1797 expire_channels(UNUSED_ARG(void *data
))
1799 struct chanData
*channel
, *next
;
1800 struct userData
*user
;
1801 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1803 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1804 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1806 for(channel
= channelList
; channel
; channel
= next
)
1808 next
= channel
->next
;
1810 /* See if the channel can be expired. */
1811 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1812 || IsProtected(channel
))
1815 /* Make sure there are no high-ranking users still in the channel. */
1816 for(user
=channel
->users
; user
; user
=user
->next
)
1817 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1822 /* Unregister the channel */
1823 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1824 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1825 unregister_channel(channel
, "registration expired.");
1828 if(chanserv_conf
.channel_expire_frequency
)
1829 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1833 expire_dnrs(UNUSED_ARG(void *data
))
1835 dict_iterator_t it
, next
;
1836 struct do_not_register
*dnr
;
1838 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
1840 dnr
= iter_data(it
);
1841 next
= iter_next(it
);
1842 if(dnr
->expires
&& dnr
->expires
<= now
)
1843 dict_remove(handle_dnrs
, dnr
->chan_name
+ 1);
1845 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
1847 dnr
= iter_data(it
);
1848 next
= iter_next(it
);
1849 if(dnr
->expires
&& dnr
->expires
<= now
)
1850 dict_remove(plain_dnrs
, dnr
->chan_name
+ 1);
1852 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
1854 dnr
= iter_data(it
);
1855 next
= iter_next(it
);
1856 if(dnr
->expires
&& dnr
->expires
<= now
)
1857 dict_remove(mask_dnrs
, dnr
->chan_name
+ 1);
1860 if(chanserv_conf
.dnr_expire_frequency
)
1861 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
1865 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1867 char protect
= channel
->chOpts
[chProtect
];
1868 struct userData
*cs_victim
, *cs_aggressor
;
1870 /* If victim access level is greater than set invitelevel, don't let
1871 * us kick them, but don't consider it punishment if someone else does
1875 if(victim
== aggressor
)
1877 /* Don't protect if the victim isn't authenticated (because they
1878 can't be a channel user), unless we are to protect non-users
1881 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1883 /* If they have enough access to invite themselvs through a ban,
1884 * and its us kicking them, don't. -Rubin */
1885 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1891 if(protect
!= 'a' && !cs_victim
)
1894 /* Protect if the aggressor isn't a user because at this point,
1895 the aggressor can only be less than or equal to the victim. */
1897 /* Not protected from chanserv except above */
1898 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1899 if(aggressor
== chanserv
)
1902 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1906 /* If the aggressor was a user, then the victim can't be helped. */
1913 if(cs_victim
->access
> cs_aggressor
->access
)
1918 if(cs_victim
->access
>= cs_aggressor
->access
)
1927 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1929 struct chanData
*cData
= channel
->channel_info
;
1930 struct userData
*cs_victim
;
1932 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1933 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1934 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1937 reply("CSMSG_OPBY_LOCKED");
1939 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1947 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1949 struct chanData
*cData
= channel
->channel_info
;
1950 struct userData
*cs_victim
;
1952 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1953 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1954 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1956 reply("CSMSG_HOPBY_LOCKED");
1965 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1967 if(IsService(victim
))
1969 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1973 if(protect_user(victim
, user
, channel
->channel_info
, false))
1975 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1983 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1985 if(IsService(victim
))
1987 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1991 if(protect_user(victim
, user
, channel
->channel_info
, false))
1993 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2000 static struct do_not_register
*
2001 chanserv_add_dnr(const char *chan_name
, const char *setter
, time_t expires
, const char *reason
)
2003 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
2004 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
2005 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
2006 strcpy(dnr
->reason
, reason
);
2008 dnr
->expires
= expires
;
2009 if(dnr
->chan_name
[0] == '*')
2010 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
2011 else if(strpbrk(dnr
->chan_name
, "*?"))
2012 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
2014 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
2018 static struct dnrList
2019 chanserv_find_dnrs(const char *chan_name
, const char *handle
, unsigned int max
)
2021 struct dnrList list
;
2022 dict_iterator_t it
, next
;
2023 struct do_not_register
*dnr
;
2025 dnrList_init(&list
);
2027 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
2029 if(dnr
->expires
&& dnr
->expires
<= now
)
2030 dict_remove(handle_dnrs
, handle
);
2031 else if (list
.used
< max
)
2032 dnrList_append(&list
, dnr
);
2035 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
2037 if(dnr
->expires
&& dnr
->expires
<= now
)
2038 dict_remove(plain_dnrs
, chan_name
);
2039 else if (list
.used
< max
)
2040 dnrList_append(&list
, dnr
);
2044 for(it
= dict_first(mask_dnrs
); it
&& list
.used
< max
; it
= next
)
2046 next
= iter_next(it
);
2047 if(!match_ircglob(chan_name
, iter_key(it
)))
2049 dnr
= iter_data(it
);
2050 if(dnr
->expires
&& dnr
->expires
<= now
)
2051 dict_remove(mask_dnrs
, iter_key(it
));
2053 dnrList_append(&list
, dnr
);
2059 static int dnr_print_func(struct do_not_register
*dnr
, void *extra
)
2061 struct userNode
*user
;
2062 char buf1
[INTERVALLEN
];
2063 char buf2
[INTERVALLEN
];
2067 strftime(buf1
, sizeof(buf1
), "%d %b %Y", localtime(&dnr
->set
));
2070 strftime(buf2
, sizeof(buf2
), "%d %b %Y", localtime(&dnr
->expires
));
2071 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET_EXPIRES", dnr
->chan_name
, buf1
, dnr
->setter
, buf2
, dnr
->reason
);
2075 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf1
, dnr
->setter
, dnr
->reason
);
2078 send_message(user
, chanserv
, "CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2083 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
2085 struct dnrList list
;
2088 list
= chanserv_find_dnrs(chan_name
, handle
, UINT_MAX
);
2089 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
2090 dnr_print_func(list
.list
[ii
], user
);
2092 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2097 struct do_not_register
*
2098 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2100 struct dnrList list
;
2101 struct do_not_register
*dnr
;
2103 list
= chanserv_find_dnrs(chan_name
, handle
? handle
->handle
: NULL
, 1);
2104 dnr
= list
.used
? list
.list
[0] : NULL
;
2109 static unsigned int send_dnrs(struct userNode
*user
, dict_t dict
)
2111 struct do_not_register
*dnr
;
2112 dict_iterator_t it
, next
;
2113 unsigned int matches
= 0;
2115 for(it
= dict_first(dict
); it
; it
= next
)
2117 dnr
= iter_data(it
);
2118 next
= iter_next(it
);
2119 if(dnr
->expires
&& dnr
->expires
<= now
)
2121 dict_remove(dict
, iter_key(it
));
2124 dnr_print_func(dnr
, user
);
2131 static CHANSERV_FUNC(cmd_noregister
)
2135 time_t expiry
, duration
;
2136 unsigned int matches
;
2140 reply("CSMSG_DNR_SEARCH_RESULTS");
2141 matches
= send_dnrs(user
, handle_dnrs
);
2142 matches
+= send_dnrs(user
, plain_dnrs
);
2143 matches
+= send_dnrs(user
, mask_dnrs
);
2145 reply("MSG_MATCH_COUNT", matches
);
2147 reply("MSG_NO_MATCHES");
2153 if(!IsChannelName(target
) && (*target
!= '*'))
2155 reply("CSMSG_NOT_DNR", target
);
2163 reply("MSG_INVALID_DURATION", argv
[2]);
2167 if(!strcmp(argv
[2], "0"))
2169 else if((duration
= ParseInterval(argv
[2])))
2170 expiry
= now
+ duration
;
2173 reply("MSG_INVALID_DURATION", argv
[2]);
2177 reason
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
2179 if((*target
== '*') && !get_handle_info(target
+ 1))
2181 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2184 chanserv_add_dnr(target
, user
->handle_info
->handle
, expiry
, reason
);
2185 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2189 reply("CSMSG_DNR_SEARCH_RESULTS");
2190 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2193 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2195 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2197 reply("MSG_NO_MATCHES");
2201 static CHANSERV_FUNC(cmd_allowregister
)
2203 const char *chan_name
= argv
[1];
2205 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2206 || dict_remove(plain_dnrs
, chan_name
)
2207 || dict_remove(mask_dnrs
, chan_name
))
2209 reply("CSMSG_DNR_REMOVED", chan_name
);
2212 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2217 struct userNode
*source
;
2221 time_t min_set
, max_set
;
2222 time_t min_expires
, max_expires
;
2227 dnr_search_matches(const struct do_not_register
*dnr
, const struct dnr_search
*search
)
2229 return !((dnr
->set
< search
->min_set
)
2230 || (dnr
->set
> search
->max_set
)
2231 || (dnr
->expires
< search
->min_expires
)
2232 || (search
->max_expires
2233 && ((dnr
->expires
== 0)
2234 || (dnr
->expires
> search
->max_expires
)))
2235 || (search
->chan_mask
2236 && !match_ircglob(dnr
->chan_name
, search
->chan_mask
))
2237 || (search
->setter_mask
2238 && !match_ircglob(dnr
->setter
, search
->setter_mask
))
2239 || (search
->reason_mask
2240 && !match_ircglob(dnr
->reason
, search
->reason_mask
)));
2243 static struct dnr_search
*
2244 dnr_search_create(struct userNode
*user
, struct svccmd
*cmd
, unsigned int argc
, char *argv
[])
2246 struct dnr_search
*discrim
;
2249 discrim
= calloc(1, sizeof(*discrim
));
2250 discrim
->source
= user
;
2251 discrim
->chan_mask
= NULL
;
2252 discrim
->setter_mask
= NULL
;
2253 discrim
->reason_mask
= NULL
;
2254 discrim
->max_set
= INT_MAX
;
2255 discrim
->limit
= 50;
2257 for(ii
=0; ii
<argc
; ++ii
)
2261 reply("MSG_MISSING_PARAMS", argv
[ii
]);
2264 else if(0 == irccasecmp(argv
[ii
], "channel"))
2266 discrim
->chan_mask
= argv
[++ii
];
2268 else if(0 == irccasecmp(argv
[ii
], "setter"))
2270 discrim
->setter_mask
= argv
[++ii
];
2272 else if(0 == irccasecmp(argv
[ii
], "reason"))
2274 discrim
->reason_mask
= argv
[++ii
];
2276 else if(0 == irccasecmp(argv
[ii
], "limit"))
2278 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 0);
2280 else if(0 == irccasecmp(argv
[ii
], "set"))
2282 const char *cmp
= argv
[++ii
];
2285 discrim
->min_set
= now
- ParseInterval(cmp
+ 2);
2287 discrim
->min_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2288 } else if(cmp
[0] == '=') {
2289 discrim
->min_set
= discrim
->max_set
= now
- ParseInterval(cmp
+ 1);
2290 } else if(cmp
[0] == '>') {
2292 discrim
->max_set
= now
- ParseInterval(cmp
+ 2);
2294 discrim
->max_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2296 discrim
->max_set
= now
- (ParseInterval(cmp
) - 1);
2299 else if(0 == irccasecmp(argv
[ii
], "expires"))
2301 const char *cmp
= argv
[++ii
];
2304 discrim
->max_expires
= now
+ ParseInterval(cmp
+ 2);
2306 discrim
->max_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2307 } else if(cmp
[0] == '=') {
2308 discrim
->min_expires
= discrim
->max_expires
= now
+ ParseInterval(cmp
+ 1);
2309 } else if(cmp
[0] == '>') {
2311 discrim
->min_expires
= now
+ ParseInterval(cmp
+ 2);
2313 discrim
->min_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2315 discrim
->min_expires
= now
+ (ParseInterval(cmp
) - 1);
2320 reply("MSG_INVALID_CRITERIA", argv
[ii
]);
2331 typedef int (*dnr_search_func
)(struct do_not_register
*match
, void *extra
);
2334 dnr_search(struct dnr_search
*discrim
, dnr_search_func dsf
, void *data
)
2336 struct do_not_register
*dnr
;
2337 dict_iterator_t next
;
2342 /* Initialize local variables. */
2345 if(discrim
->chan_mask
)
2347 int shift
= (discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*') ? 2 : 0;
2348 if('\0' == discrim
->chan_mask
[shift
+ strcspn(discrim
->chan_mask
+shift
, "*?")])
2352 if(target_fixed
&& discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*')
2354 /* Check against account-based DNRs. */
2355 dnr
= dict_find(handle_dnrs
, discrim
->chan_mask
+ 2, NULL
);
2356 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2359 else if(target_fixed
)
2361 /* Check against channel-based DNRs. */
2362 dnr
= dict_find(plain_dnrs
, discrim
->chan_mask
, NULL
);
2363 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2368 /* Exhaustively search account DNRs. */
2369 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
2371 next
= iter_next(it
);
2372 dnr
= iter_data(it
);
2373 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2377 /* Do the same for channel DNRs. */
2378 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
2380 next
= iter_next(it
);
2381 dnr
= iter_data(it
);
2382 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2386 /* Do the same for wildcarded channel DNRs. */
2387 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
2389 next
= iter_next(it
);
2390 dnr
= iter_data(it
);
2391 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2399 dnr_remove_func(struct do_not_register
*match
, void *extra
)
2401 struct userNode
*user
;
2404 chan_name
= alloca(strlen(match
->chan_name
) + 1);
2405 strcpy(chan_name
, match
->chan_name
);
2407 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2408 || dict_remove(plain_dnrs
, chan_name
)
2409 || dict_remove(mask_dnrs
, chan_name
))
2411 send_message(user
, chanserv
, "CSMSG_DNR_REMOVED", chan_name
);
2417 dnr_count_func(struct do_not_register
*match
, void *extra
)
2419 return 0; (void)match
; (void)extra
;
2422 static MODCMD_FUNC(cmd_dnrsearch
)
2424 struct dnr_search
*discrim
;
2425 dnr_search_func action
;
2426 struct svccmd
*subcmd
;
2427 unsigned int matches
;
2430 sprintf(buf
, "dnrsearch %s", argv
[1]);
2431 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
2434 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2437 if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
2439 if(!irccasecmp(argv
[1], "print"))
2440 action
= dnr_print_func
;
2441 else if(!irccasecmp(argv
[1], "remove"))
2442 action
= dnr_remove_func
;
2443 else if(!irccasecmp(argv
[1], "count"))
2444 action
= dnr_count_func
;
2447 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2451 discrim
= dnr_search_create(user
, cmd
, argc
-2, argv
+2);
2455 if(action
== dnr_print_func
)
2456 reply("CSMSG_DNR_SEARCH_RESULTS");
2457 matches
= dnr_search(discrim
, action
, user
);
2459 reply("MSG_MATCH_COUNT", matches
);
2461 reply("MSG_NO_MATCHES");
2467 chanserv_get_owned_count(struct handle_info
*hi
)
2469 struct userData
*cList
;
2472 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2473 if(cList
->access
== UL_OWNER
)
2478 static CHANSERV_FUNC(cmd_register
)
2480 struct handle_info
*handle
;
2481 struct chanData
*cData
;
2482 struct modeNode
*mn
;
2483 char reason
[MAXLEN
];
2485 unsigned int new_channel
, force
=0;
2486 struct do_not_register
*dnr
;
2489 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2490 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2496 if(channel
->channel_info
)
2498 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2502 if(channel
->bad_channel
)
2504 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2508 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2510 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2515 chan_name
= channel
->name
;
2521 reply("MSG_MISSING_PARAMS", cmd
->name
);
2522 svccmd_send_help_brief(user
, chanserv
, cmd
);
2525 if(!IsChannelName(argv
[1]))
2527 reply("MSG_NOT_CHANNEL_NAME");
2531 if(opserv_bad_channel(argv
[1]))
2533 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2538 chan_name
= argv
[1];
2541 if(argc
>= (new_channel
+2))
2543 if(!IsHelping(user
))
2545 reply("CSMSG_PROXY_FORBIDDEN");
2549 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2551 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2552 dnr
= chanserv_is_dnr(chan_name
, handle
);
2554 /* Check if they are over the limit.. */
2555 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2557 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2564 handle
= user
->handle_info
;
2565 dnr
= chanserv_is_dnr(chan_name
, handle
);
2566 /* Check if they are over the limit.. */
2567 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2569 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2572 /* Check if another service is in the channel */
2574 for(n
= 0; n
< channel
->members
.used
; n
++)
2576 mn
= channel
->members
.list
[n
];
2577 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2579 reply("CSMSG_ANOTHER_SERVICE");
2586 if(!IsHelping(user
))
2587 reply("CSMSG_DNR_CHANNEL", chan_name
);
2589 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2593 /* now handled above for message specilization *
2594 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2596 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2601 if (chanserv_conf
.valid_channel_regex_set
) {
2602 int err
= regexec(&chanserv_conf
.valid_channel_regex
, chan_name
, 0, 0, 0);
2605 buff
[regerror(err
, &chanserv_conf
.valid_channel_regex
, buff
, sizeof(buff
))] = 0;
2606 log_module(CS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
2608 if(err
== REG_NOMATCH
) {
2609 reply("CSMSG_ILLEGAL_CHANNEL", chan_name
);
2615 channel
= AddChannel(chan_name
, now
, NULL
, NULL
, NULL
);
2617 cData
= register_channel(channel
, user
->handle_info
->handle
);
2618 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2619 cData
->modes
= chanserv_conf
.default_modes
;
2621 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2622 if (IsOffChannel(cData
))
2624 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2628 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2629 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2630 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2632 mod_chanmode_announce(chanserv
, channel
, change
);
2633 mod_chanmode_free(change
);
2636 /* Initialize the channel's max user record. */
2637 cData
->max
= channel
->members
.used
;
2638 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2640 if(handle
!= user
->handle_info
)
2641 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2644 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2645 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2646 handle
->handle
, user
->handle_info
->handle
);
2651 make_confirmation_string(struct userData
*uData
)
2653 static char strbuf
[16];
2658 for(src
= uData
->handle
->handle
; *src
; )
2659 accum
= accum
* 31 + toupper(*src
++);
2661 for(src
= uData
->channel
->channel
->name
; *src
; )
2662 accum
= accum
* 31 + toupper(*src
++);
2663 sprintf(strbuf
, "%08x", accum
);
2667 static CHANSERV_FUNC(cmd_unregister
)
2670 char reason
[MAXLEN
];
2671 struct chanData
*cData
;
2672 struct userData
*uData
;
2674 cData
= channel
->channel_info
;
2677 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2681 uData
= GetChannelUser(cData
, user
->handle_info
);
2682 if(!uData
|| (uData
->access
< UL_OWNER
))
2684 reply("CSMSG_NO_ACCESS");
2688 if(IsProtected(cData
))
2690 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2694 if(!IsHelping(user
))
2696 const char *confirm_string
;
2697 if(IsSuspended(cData
))
2699 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2702 confirm_string
= make_confirmation_string(uData
);
2703 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2705 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2710 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2711 name
= strdup(channel
->name
);
2712 unregister_channel(cData
, reason
);
2713 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2714 reply("CSMSG_UNREG_SUCCESS", name
);
2720 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2722 extern struct userNode
*spamserv
;
2723 struct mod_chanmode
*change
;
2725 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2727 change
= mod_chanmode_alloc(2);
2729 change
->args
[0].mode
= MODE_CHANOP
;
2730 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2731 change
->args
[1].mode
= MODE_CHANOP
;
2732 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2736 change
= mod_chanmode_alloc(1);
2738 change
->args
[0].mode
= MODE_CHANOP
;
2739 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2742 mod_chanmode_announce(chanserv
, channel
, change
);
2743 mod_chanmode_free(change
);
2746 static CHANSERV_FUNC(cmd_move
)
2748 struct mod_chanmode change
;
2749 struct chanNode
*target
;
2750 struct modeNode
*mn
;
2751 struct userData
*uData
;
2752 struct do_not_register
*dnr
;
2753 int chanserv_join
= 0, spamserv_join
;
2757 if(IsProtected(channel
->channel_info
))
2759 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2763 if(!IsChannelName(argv
[1]))
2765 reply("MSG_NOT_CHANNEL_NAME");
2769 if(opserv_bad_channel(argv
[1]))
2771 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2775 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2777 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2779 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2781 if(!IsHelping(user
))
2782 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2784 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2790 mod_chanmode_init(&change
);
2791 if(!(target
= GetChannel(argv
[1])))
2793 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2794 if(!IsSuspended(channel
->channel_info
))
2797 else if(target
->channel_info
)
2799 reply("CSMSG_ALREADY_REGGED", target
->name
);
2802 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2803 && !IsHelping(user
))
2805 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2808 else if(!IsSuspended(channel
->channel_info
))
2813 /* Clear MODE_REGISTERED from old channel, add it to new. */
2815 change
.modes_clear
= MODE_REGISTERED
;
2816 mod_chanmode_announce(chanserv
, channel
, &change
);
2817 change
.modes_clear
= 0;
2818 change
.modes_set
= MODE_REGISTERED
;
2819 mod_chanmode_announce(chanserv
, target
, &change
);
2822 /* Move the channel_info to the target channel; it
2823 shouldn't be necessary to clear timeq callbacks
2824 for the old channel. */
2825 target
->channel_info
= channel
->channel_info
;
2826 target
->channel_info
->channel
= target
;
2827 channel
->channel_info
= NULL
;
2829 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2832 ss_cs_join_channel(target
, spamserv_join
);
2834 if(!IsSuspended(target
->channel_info
))
2836 char reason2
[MAXLEN
];
2837 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2838 DelChannelUser(chanserv
, channel
, reason2
, 0);
2841 UnlockChannel(channel
);
2842 LockChannel(target
);
2843 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2844 channel
->name
, target
->name
, user
->handle_info
->handle
);
2846 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2851 merge_users(struct chanData
*source
, struct chanData
*target
)
2853 struct userData
*suData
, *tuData
, *next
;
2859 /* Insert the source's users into the scratch area. */
2860 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2861 dict_insert(merge
, suData
->handle
->handle
, suData
);
2863 /* Iterate through the target's users, looking for
2864 users common to both channels. The lower access is
2865 removed from either the scratch area or target user
2867 for(tuData
= target
->users
; tuData
; tuData
= next
)
2869 struct userData
*choice
;
2871 next
= tuData
->next
;
2873 /* If a source user exists with the same handle as a target
2874 channel's user, resolve the conflict by removing one. */
2875 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2879 /* Pick the data we want to keep. */
2880 /* If the access is the same, use the later seen time. */
2881 if(suData
->access
== tuData
->access
)
2882 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2883 else /* Otherwise, keep the higher access level. */
2884 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2886 /* Remove the user that wasn't picked. */
2887 if(choice
== tuData
)
2889 dict_remove(merge
, suData
->handle
->handle
);
2890 del_channel_user(suData
, 0);
2893 del_channel_user(tuData
, 0);
2896 /* Move the remaining users to the target channel. */
2897 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2899 suData
= iter_data(it
);
2901 /* Insert the user into the target channel's linked list. */
2902 suData
->prev
= NULL
;
2903 suData
->next
= target
->users
;
2904 suData
->channel
= target
;
2907 target
->users
->prev
= suData
;
2908 target
->users
= suData
;
2910 /* Update the user counts for the target channel; the
2911 source counts are left alone. */
2912 target
->userCount
++;
2915 /* Possible to assert (source->users == NULL) here. */
2916 source
->users
= NULL
;
2921 merge_bans(struct chanData
*source
, struct chanData
*target
)
2923 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2925 /* Hold on to the original head of the target ban list
2926 to avoid comparing source bans with source bans. */
2927 tFront
= target
->bans
;
2929 /* Perform a totally expensive O(n*m) merge, ick. */
2930 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2932 /* Flag to track whether the ban's been moved
2933 to the destination yet. */
2936 /* Possible to assert (sbData->prev == NULL) here. */
2937 sNext
= sbData
->next
;
2939 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2941 tNext
= tbData
->next
;
2943 /* Perform two comparisons between each source
2944 and target ban, conflicts are resolved by
2945 keeping the broader ban and copying the later
2946 expiration and triggered time. */
2947 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2949 /* There is a broader ban in the target channel that
2950 overrides one in the source channel; remove the
2951 source ban and break. */
2952 if(sbData
->expires
> tbData
->expires
)
2953 tbData
->expires
= sbData
->expires
;
2954 if(sbData
->triggered
> tbData
->triggered
)
2955 tbData
->triggered
= sbData
->triggered
;
2956 del_channel_ban(sbData
);
2959 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2961 /* There is a broader ban in the source channel that
2962 overrides one in the target channel; remove the
2963 target ban, fall through and move the source over. */
2964 if(tbData
->expires
> sbData
->expires
)
2965 sbData
->expires
= tbData
->expires
;
2966 if(tbData
->triggered
> sbData
->triggered
)
2967 sbData
->triggered
= tbData
->triggered
;
2968 if(tbData
== tFront
)
2970 del_channel_ban(tbData
);
2973 /* Source bans can override multiple target bans, so
2974 we allow a source to run through this loop multiple
2975 times, but we can only move it once. */
2980 /* Remove the source ban from the source ban list. */
2982 sbData
->next
->prev
= sbData
->prev
;
2984 /* Modify the source ban's associated channel. */
2985 sbData
->channel
= target
;
2987 /* Insert the ban into the target channel's linked list. */
2988 sbData
->prev
= NULL
;
2989 sbData
->next
= target
->bans
;
2992 target
->bans
->prev
= sbData
;
2993 target
->bans
= sbData
;
2995 /* Update the user counts for the target channel. */
3000 /* Possible to assert (source->bans == NULL) here. */
3001 source
->bans
= NULL
;
3005 merge_data(struct chanData
*source
, struct chanData
*target
)
3007 /* Use more recent visited and owner-transfer time; use older
3008 * registered time. Bitwise or may_opchan. Use higher max.
3009 * Do not touch last_refresh, ban count or user counts.
3011 if(source
->visited
> target
->visited
)
3012 target
->visited
= source
->visited
;
3013 if(source
->registered
< target
->registered
)
3014 target
->registered
= source
->registered
;
3015 if(source
->ownerTransfer
> target
->ownerTransfer
)
3016 target
->ownerTransfer
= source
->ownerTransfer
;
3017 if(source
->may_opchan
)
3018 target
->may_opchan
= 1;
3019 if(source
->max
> target
->max
)
3020 target
->max
= source
->max
;
3024 merge_channel(struct chanData
*source
, struct chanData
*target
)
3026 merge_users(source
, target
);
3027 merge_bans(source
, target
);
3028 merge_data(source
, target
);
3031 static CHANSERV_FUNC(cmd_merge
)
3033 struct userData
*target_user
;
3034 struct chanNode
*target
;
3035 char reason
[MAXLEN
];
3040 /* Make sure the target channel exists and is registered to the user
3041 performing the command. */
3042 if(!(target
= GetChannel(argv
[1])))
3044 reply("MSG_INVALID_CHANNEL");
3049 if (!irccasecmp("nodelete", argv
[2]))
3053 if(!target
->channel_info
)
3055 reply("CSMSG_NOT_REGISTERED", target
->name
);
3059 if(IsProtected(channel
->channel_info
))
3061 reply("CSMSG_MERGE_NODELETE");
3065 if(IsSuspended(target
->channel_info
))
3067 reply("CSMSG_MERGE_SUSPENDED");
3071 if(channel
== target
)
3073 reply("CSMSG_MERGE_SELF");
3077 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
3078 if(!target_user
|| (target_user
->access
< UL_OWNER
))
3080 reply("CSMSG_MERGE_NOT_OWNER");
3084 /* Merge the channel structures and associated data. */
3085 merge_channel(channel
->channel_info
, target
->channel_info
);
3086 spamserv_cs_move_merge(user
, channel
, target
, 0);
3087 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
3089 unregister_channel(channel
->channel_info
, reason
);
3090 reply("CSMSG_MERGE_SUCCESS", target
->name
);
3094 static CHANSERV_FUNC(cmd_opchan
)
3096 struct mod_chanmode change
;
3097 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
3099 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
3102 if(!IsInChannel(channel
,chanserv
)) {
3103 reply("CSMSG_NOT_IN_CHANNEL", channel
->name
);
3106 channel
->channel_info
->may_opchan
= 0;
3107 mod_chanmode_init(&change
);
3109 change
.args
[0].mode
= MODE_CHANOP
;
3110 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
3111 if(!change
.args
[0].u
.member
)
3113 reply("CSMSG_OUT_OF_CHANNEL", channel
->name
);
3116 mod_chanmode_announce(chanserv
, channel
, &change
);
3117 reply("CSMSG_OPCHAN_DONE", channel
->name
);
3121 static CHANSERV_FUNC(cmd_adduser
)
3123 struct userData
*actee
;
3124 struct userData
*actor
, *real_actor
;
3125 struct handle_info
*handle
= NULL
;
3126 struct adduserPending
*tmp
;
3127 unsigned short access_level
, override
= 0;
3131 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
3133 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
3137 access_level
= user_level_from_name(argv
[2], UL_OWNER
);
3140 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3144 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3145 real_actor
= GetTrueChannelAccess(channel
->channel_info
, user
->handle_info
);
3147 if(actor
->access
<= access_level
)
3149 reply("CSMSG_NO_BUMP_ACCESS");
3153 /* Trying to add someone with equal/more access */
3154 if (!real_actor
|| real_actor
->access
<= access_level
)
3155 override
= CMD_LOG_OVERRIDE
;
3157 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3159 /* 'kevin must first authenticate with AuthServ'. is sent to user */
3160 struct userNode
*unode
;
3161 unode
= GetUserH(argv
[1]); /* find user struct by nick */
3164 if(find_adduser_pending(channel
, unode
)) {
3165 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
3168 if(IsInChannel(channel
, unode
)) {
3169 reply("CSMSG_ADDUSER_PENDING");
3170 tmp
= add_adduser_pending(channel
, unode
, access_level
);
3171 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
3173 /* this results in user must auth AND not in chan errors. too confusing..
3175 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3183 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3185 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
3189 time_t accessexpiry
= 0;
3190 unsigned int duration
= 0;
3192 if ((duration
= ParseInterval(argv
[3])))
3193 accessexpiry
= now
+ duration
;
3196 actee
= add_channel_user(channel
->channel_info
, handle
, access_level
, 0, NULL
, accessexpiry
);
3197 scan_user_presence(actee
, NULL
);
3200 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
3202 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access_level
), access_level
);
3203 return 1 | override
;
3206 static CHANSERV_FUNC(cmd_clvl
)
3208 struct handle_info
*handle
;
3209 struct userData
*victim
;
3210 struct userData
*actor
, *real_actor
;
3211 unsigned short new_access
, override
= 0;
3212 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3216 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3217 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3219 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3222 if(handle
== user
->handle_info
&& !privileged
)
3224 reply("CSMSG_NO_SELF_CLVL");
3228 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3230 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3234 if(actor
->access
<= victim
->access
&& !privileged
)
3236 reply("MSG_USER_OUTRANKED", handle
->handle
);
3240 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
3244 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3248 if(new_access
>= actor
->access
&& !privileged
)
3250 reply("CSMSG_NO_BUMP_ACCESS");
3254 time_t clvlexpiry
= 0;
3255 unsigned int duration
= 0;
3257 if ((duration
= ParseInterval(argv
[3])))
3258 clvlexpiry
= now
+ duration
;
3262 if (victim
->accessexpiry
> 0) {
3263 reply("CSMSG_NO_BUMP_EXPIRY");
3267 victim
->clvlexpiry
= clvlexpiry
;
3268 victim
->lastaccess
= victim
->access
;
3269 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
3272 /* Trying to clvl a equal/higher user */
3273 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& handle
!= user
->handle_info
))
3274 override
= CMD_LOG_OVERRIDE
;
3275 /* Trying to clvl someone to equal/higher access */
3276 if(!real_actor
|| new_access
>= real_actor
->access
)
3277 override
= CMD_LOG_OVERRIDE
;
3278 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3279 * If they lower their own access it's not a big problem.
3281 victim
->access
= new_access
;
3282 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
3283 return 1 | override
;
3286 static CHANSERV_FUNC(cmd_deluser
)
3288 struct handle_info
*handle
;
3289 struct userData
*victim
;
3290 struct userData
*actor
, *real_actor
;
3291 unsigned short access_level
, override
= 0;
3296 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3297 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3299 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
3302 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3304 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3310 access_level
= user_level_from_name(argv
[1], UL_OWNER
);
3311 char *useraccess
= user_level_name_from_level(victim
->access
);
3314 reply("CSMSG_INVALID_ACCESS", argv
[1]);
3317 if(strcasecmp(argv
[1], useraccess
))
3319 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
3325 access_level
= victim
->access
;
3328 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
3330 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
3334 /* If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override */
3335 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& real_actor
!= victim
))
3336 override
= CMD_LOG_OVERRIDE
;
3338 chan_name
= strdup(channel
->name
);
3339 del_channel_user(victim
, 1);
3340 reply("CSMSG_DELETED_USER", handle
->handle
, access_level
, chan_name
);
3342 return 1 | override
;
3346 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
3348 struct userData
*actor
, *real_actor
, *uData
, *next
;
3349 unsigned int override
= 0;
3351 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3352 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3354 if(min_access
> max_access
)
3356 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3360 if((actor
->access
<= max_access
) && !IsHelping(user
))
3362 reply("CSMSG_NO_ACCESS");
3366 if(!real_actor
|| real_actor
->access
<= max_access
)
3367 override
= CMD_LOG_OVERRIDE
;
3369 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3373 if((uData
->access
>= min_access
)
3374 && (uData
->access
<= max_access
)
3375 && match_ircglob(uData
->handle
->handle
, mask
))
3376 del_channel_user(uData
, 1);
3379 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3380 return 1 | override
;
3383 static CHANSERV_FUNC(cmd_mdelowner
)
3385 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3388 static CHANSERV_FUNC(cmd_mdelcoowner
)
3390 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3393 static CHANSERV_FUNC(cmd_mdelmanager
)
3395 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3398 static CHANSERV_FUNC(cmd_mdelop
)
3400 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3403 static CHANSERV_FUNC(cmd_mdelhalfop
)
3405 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3408 static CHANSERV_FUNC(cmd_mdelpeon
)
3410 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3413 static CHANSERV_FUNC(cmd_mdelpal
)
3415 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3418 static CHANSERV_FUNC(cmd_levels
)
3420 struct helpfile_table tbl
;
3423 tbl
.length
= 6 + 1; // 6 levels
3426 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3427 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3428 tbl
.contents
[0][0] = "Level";
3429 tbl
.contents
[0][1] = "From";
3430 tbl
.contents
[0][2] = "-";
3431 tbl
.contents
[0][3] = "To";
3433 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3434 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3435 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3436 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3437 tbl
.contents
[ii
][3] = msnprintf(1, "");
3439 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3440 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3441 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3442 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3443 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3445 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3446 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3447 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3448 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3449 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3451 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3452 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3453 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3454 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3455 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3457 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3458 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3459 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3460 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3461 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3463 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3464 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3465 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3466 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3467 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3469 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3473 reply("CSMSG_LEVELS_HEADER");
3474 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3475 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3476 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3477 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3478 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3479 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3486 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3488 struct banData
*bData
, *next
;
3489 char interval
[INTERVALLEN
];
3494 limit
= now
- duration
;
3495 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3499 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3502 del_channel_ban(bData
);
3506 intervalString(interval
, duration
, user
->handle_info
);
3507 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3512 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
)
3514 struct userData
*actor
, *uData
, *next
;
3515 char interval
[INTERVALLEN
];
3519 actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3520 if(min_access
> max_access
)
3522 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3526 if(!actor
|| actor
->access
<= max_access
)
3528 reply("CSMSG_NO_ACCESS");
3533 limit
= now
- duration
;
3534 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3538 if((uData
->seen
> limit
)
3540 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3543 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3544 || (!max_access
&& (uData
->access
< actor
->access
)))
3546 del_channel_user(uData
, 1);
3554 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3556 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3560 static CHANSERV_FUNC(cmd_trim
)
3562 unsigned long duration
;
3563 unsigned short min_level
, max_level
;
3568 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3569 duration
= ParseInterval(argv
[2]);
3572 reply("CSMSG_CANNOT_TRIM");
3576 if(!irccasecmp(argv
[1], "lamers"))
3578 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3581 else if(!irccasecmp(argv
[1], "users"))
3583 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3586 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3588 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3591 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3593 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3598 reply("CSMSG_INVALID_TRIM", argv
[1]);
3603 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3604 to the user. cmd_all takes advantage of this. */
3605 static CHANSERV_FUNC(cmd_up
)
3607 struct mod_chanmode change
;
3608 struct userData
*uData
;
3611 mod_chanmode_init(&change
);
3613 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3614 if(!change
.args
[0].u
.member
)
3617 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3621 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3625 reply("CSMSG_GODMODE_UP", argv
[0]);
3628 else if(uData
->access
>= UL_OP
)
3630 change
.args
[0].mode
= MODE_CHANOP
;
3631 errmsg
= "CSMSG_ALREADY_OPPED";
3633 else if(uData
->access
>= UL_HALFOP
)
3635 change
.args
[0].mode
= MODE_HALFOP
;
3636 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3638 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3640 change
.args
[0].mode
= MODE_VOICE
;
3641 errmsg
= "CSMSG_ALREADY_VOICED";
3646 reply("CSMSG_NO_ACCESS");
3649 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3650 if(!change
.args
[0].mode
)
3653 reply(errmsg
, channel
->name
);
3656 modcmd_chanmode_announce(&change
);
3660 static CHANSERV_FUNC(cmd_down
)
3662 struct mod_chanmode change
;
3664 mod_chanmode_init(&change
);
3666 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3667 if(!change
.args
[0].u
.member
)
3670 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3674 if(!change
.args
[0].u
.member
->modes
)
3677 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3681 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3682 modcmd_chanmode_announce(&change
);
3686 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
)
3688 struct userData
*cList
;
3690 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3692 if(IsSuspended(cList
->channel
)
3693 || IsUserSuspended(cList
)
3694 || !GetUserMode(cList
->channel
->channel
, user
))
3697 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3703 static CHANSERV_FUNC(cmd_upall
)
3705 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3708 static CHANSERV_FUNC(cmd_downall
)
3710 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3713 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3714 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3717 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
)
3719 unsigned int ii
, valid
;
3720 struct userNode
*victim
;
3721 struct mod_chanmode
*change
;
3723 change
= mod_chanmode_alloc(argc
- 1);
3725 for(ii
=valid
=0; ++ii
< argc
; )
3727 if(!(victim
= GetUserH(argv
[ii
])))
3729 change
->args
[valid
].mode
= mode
;
3730 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3731 if(!change
->args
[valid
].u
.member
)
3733 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3738 change
->argc
= valid
;
3739 if(valid
< (argc
-1))
3740 reply("CSMSG_PROCESS_FAILED");
3743 modcmd_chanmode_announce(change
);
3744 reply(action
, channel
->name
);
3746 mod_chanmode_free(change
);
3750 static CHANSERV_FUNC(cmd_op
)
3752 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3755 static CHANSERV_FUNC(cmd_hop
)
3757 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3760 static CHANSERV_FUNC(cmd_deop
)
3762 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3765 static CHANSERV_FUNC(cmd_dehop
)
3767 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3770 static CHANSERV_FUNC(cmd_voice
)
3772 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3775 static CHANSERV_FUNC(cmd_devoice
)
3777 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3781 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3788 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3790 struct modeNode
*mn
= channel
->members
.list
[ii
];
3792 if(IsService(mn
->user
))
3795 b
= user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
3801 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3805 victims
[(*victimCount
)++] = mn
;
3811 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3813 struct userNode
*victim
;
3814 struct modeNode
**victims
;
3815 unsigned int offset
, n
, victimCount
, duration
= 0;
3817 char *reason
= "Bye.", *ban
, *name
;
3818 char interval
[INTERVALLEN
];
3820 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3821 REQUIRE_PARAMS(offset
);
3824 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3825 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3827 /* Truncate the reason to a length of TOPICLEN, as
3828 the ircd does; however, leave room for an ellipsis
3829 and the kicker's nick. */
3830 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3834 if((victim
= GetUserH(argv
[1])))
3836 victims
= alloca(sizeof(victims
[0]));
3837 victims
[0] = GetUserMode(channel
, victim
);
3838 /* XXX: The comparison with ACTION_KICK is just because all
3839 * other actions can work on users outside the channel, and we
3840 * want to allow those (e.g. unbans) in that case. If we add
3841 * some other ejection action for in-channel users, change
3843 victimCount
= victims
[0] ? 1 : 0;
3845 if(IsService(victim
))
3848 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3852 if((action
== ACTION_KICK
) && !victimCount
)
3855 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3859 if(protect_user(victim
, user
, channel
->channel_info
, false))
3861 // This translates to send_message(user, cmd->parent->bot, ...)
3862 // if user is x3 (ctcp action) cmd is null and segfault.
3864 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3868 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3869 name
= victim
->nick
;
3871 else if(!is_ircmask(argv
[1]) && (*argv
[1] == '*'))
3873 struct handle_info
*hi
;
3874 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
3875 const char *accountname
= argv
[1] + 1;
3877 if(!(hi
= get_handle_info(accountname
)))
3879 reply("MSG_HANDLE_UNKNOWN", accountname
);
3883 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", hi
->handle
);
3884 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3886 b
= bad_channel_ban(channel
, user
, banmask
, &victimCount
, victims
);
3889 reply("CSMSG_MASK_PROTECTED", banmask
);
3893 reply("CSMSG_BAD_BAN", banmask
);
3897 if((action
== ACTION_KICK
) && (victimCount
== 0))
3899 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, banmask
);
3903 name
= ban
= strdup(banmask
);
3907 if(!is_ircmask(argv
[1]))
3910 reply("MSG_NICK_UNKNOWN", argv
[1]);
3914 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3916 b
= bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
);
3917 if(cmd
&& (b
== 1)) {
3918 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3921 else if(cmd
&& (b
== -1)) {
3922 reply("CSMSG_BAD_BAN", argv
[1]);
3925 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3926 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3928 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3929 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3930 some creativity, but its not x3's job to be the ban censor anyway. */
3931 if(is_overmask(argv
[1]))
3934 reply("CSMSG_LAME_MASK", argv
[1]);
3938 if((action
== ACTION_KICK
) && (victimCount
== 0))
3941 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3945 name
= ban
= strdup(argv
[1]);
3948 /* Truncate the ban in place if necessary; we must ensure
3949 that 'ban' is a valid ban mask before sanitizing it. */
3951 sanitize_ircmask(ban
);
3953 if(action
& ACTION_ADD_LAMER
)
3955 struct banData
*bData
, *next
;
3957 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3960 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3965 if(action
& ACTION_ADD_TIMED_LAMER
)
3967 duration
= ParseInterval(argv
[2]);
3972 reply("CSMSG_DURATION_TOO_LOW");
3976 else if(duration
> (86400 * 365 * 2))
3979 reply("CSMSG_DURATION_TOO_HIGH");
3986 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3988 if(match_ircglobs(bData
->mask
, ban
))
3990 int exact
= !irccasecmp(bData
->mask
, ban
);
3992 /* The ban is redundant; there is already a ban
3993 with the same effect in place. */
3997 free(bData
->reason
);
3998 bData
->reason
= strdup(reason
);
3999 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
4001 reply("CSMSG_REASON_CHANGE", ban
);
4005 if(exact
&& bData
->expires
)
4009 /* If the ban matches an existing one exactly,
4010 extend the expiration time if the provided
4011 duration is longer. */
4012 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
4014 bData
->expires
= now
+ duration
;
4025 /* Delete the expiration timeq entry and
4026 requeue if necessary. */
4027 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
4030 timeq_add(bData
->expires
, expire_ban
, bData
);
4034 /* automated kickban, dont reply */
4037 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
4039 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4045 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
4052 if(match_ircglobs(ban
, bData
->mask
))
4054 /* The ban we are adding makes previously existing
4055 bans redundant; silently remove them. */
4056 del_channel_ban(bData
);
4060 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
);
4062 name
= ban
= strdup(bData
->mask
);
4066 /* WHAT DOES THIS DO?? -Rubin */
4067 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
4069 extern const char *hidden_host_suffix
;
4070 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
4072 unsigned int l1
, l2
;
4075 l2
= strlen(old_name
);
4078 if(irccasecmp(ban
+ l1
- l2
, old_name
))
4080 new_mask
= malloc(MAXLEN
);
4081 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
4083 name
= ban
= new_mask
;
4088 if(action
& ACTION_BAN
)
4090 unsigned int exists
;
4091 struct mod_chanmode
*change
;
4093 if(channel
->banlist
.used
>= MAXBANS
)
4096 reply("CSMSG_BANLIST_FULL", channel
->name
);
4101 exists
= ChannelBanExists(channel
, ban
);
4102 change
= mod_chanmode_alloc(victimCount
+ 1);
4103 for(n
= 0; n
< victimCount
; ++n
)
4105 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
4106 change
->args
[n
].u
.member
= victims
[n
];
4110 change
->args
[n
].mode
= MODE_BAN
;
4111 change
->args
[n
++].u
.hostmask
= ban
;
4115 modcmd_chanmode_announce(change
);
4117 mod_chanmode_announce(chanserv
, channel
, change
);
4118 mod_chanmode_free(change
);
4120 if(exists
&& (action
== ACTION_BAN
))
4123 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
4129 if(action
& ACTION_ADD_LAMER
)
4131 char kick_reason
[MAXLEN
];
4132 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4134 for(n
= 0; n
< victimCount
; n
++) {
4135 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
4136 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4140 else if(action
& ACTION_KICK
)
4142 char kick_reason
[MAXLEN
];
4143 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4145 for(n
= 0; n
< victimCount
; n
++) {
4146 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4152 /* No response, since it was automated. */
4154 else if(action
& ACTION_ADD_LAMER
)
4157 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
4159 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4161 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
4162 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
4163 else if(action
& ACTION_BAN
)
4164 reply("CSMSG_BAN_DONE", name
, channel
->name
);
4165 else if(action
& ACTION_KICK
&& victimCount
)
4166 reply("CSMSG_KICK_DONE", name
, channel
->name
);
4172 static CHANSERV_FUNC(cmd_kickban
)
4174 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
4177 static CHANSERV_FUNC(cmd_kick
)
4179 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
4182 static CHANSERV_FUNC(cmd_ban
)
4184 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
4187 static CHANSERV_FUNC(cmd_addlamer
)
4189 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
4192 static CHANSERV_FUNC(cmd_addtimedlamer
)
4194 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
4197 static struct mod_chanmode
*
4198 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
4200 struct mod_chanmode
*change
;
4201 unsigned char *match
;
4202 unsigned int ii
, count
;
4204 match
= alloca(bans
->used
);
4207 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4209 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
4210 MATCH_USENICK
| MATCH_VISIBLE
, 0);
4217 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4219 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
4226 change
= mod_chanmode_alloc(count
);
4227 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4231 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
4232 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
4234 assert(count
== change
->argc
);
4238 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
4240 unsigned int jj
, ii
, count
;
4242 struct chanData
*channel
;
4244 struct mod_chanmode
*change
;
4246 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
4247 /* Walk through every channel */
4248 for(channel
= channelList
; channel
; channel
= channel
->next
) {
4249 switch(channel
->chOpts
[chBanTimeout
])
4251 default: case '0': continue; /* Dont remove bans in this chan */
4252 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
4253 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
4254 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
4255 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
4256 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
4259 /* First find out how many bans were going to unset */
4260 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4261 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
4265 /* At least one ban, so setup a removal */
4266 change
= mod_chanmode_alloc(count
);
4268 /* Walk over every ban in this channel.. */
4269 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4270 bn
= channel
->channel
->banlist
.list
[jj
];
4271 if (bn
->set
< bantimeout
) {
4272 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
4274 /* Add this ban to the mode change */
4275 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4276 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
4278 /* Pull this ban out of the list */
4279 banList_remove(&(channel
->channel
->banlist
), bn
);
4284 /* Send the modes to IRC */
4285 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
4287 /* free memory from strdup above */
4288 for(ii
= 0; ii
< count
; ++ii
)
4289 free((char*)change
->args
[ii
].u
.hostmask
);
4291 mod_chanmode_free(change
);
4294 /* Set this function to run again */
4295 if(chanserv_conf
.ban_timeout_frequency
)
4296 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
4301 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
4303 struct userNode
*actee
;
4309 /* may want to allow a comma delimited list of users... */
4310 if(!(actee
= GetUserH(argv
[1])))
4312 if(!is_ircmask(argv
[1]) && *argv
[1] == '*')
4314 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
4315 const char *accountname
= argv
[1] + 1;
4317 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", accountname
);
4318 mask
= strdup(banmask
);
4320 else if(!is_ircmask(argv
[1]))
4322 reply("MSG_NICK_UNKNOWN", argv
[1]);
4327 mask
= strdup(argv
[1]);
4331 /* We don't sanitize the mask here because ircu
4333 if(action
& ACTION_UNBAN
)
4335 struct mod_chanmode
*change
;
4336 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
4341 modcmd_chanmode_announce(change
);
4342 for(ii
= 0; ii
< change
->argc
; ++ii
)
4343 free((char*)change
->args
[ii
].u
.hostmask
);
4344 mod_chanmode_free(change
);
4349 if(action
& ACTION_DEL_LAMER
)
4351 struct banData
*ban
, *next
;
4353 ban
= channel
->channel_info
->bans
; /* lamers */
4357 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
4360 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
4365 del_channel_ban(ban
);
4372 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
4374 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
4380 static CHANSERV_FUNC(cmd_unban
)
4382 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
4385 static CHANSERV_FUNC(cmd_dellamer
)
4387 /* it doesn't necessarily have to remove the channel ban - may want
4388 to make that an option. */
4389 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
4392 static CHANSERV_FUNC(cmd_unbanme
)
4394 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4395 long flags
= ACTION_UNBAN
;
4397 /* remove permanent bans if the user has the proper access. */
4398 if(uData
->access
>= UL_MANAGER
)
4399 flags
|= ACTION_DEL_LAMER
;
4401 argv
[1] = user
->nick
;
4402 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
4405 static CHANSERV_FUNC(cmd_unbanall
)
4407 struct mod_chanmode
*change
;
4410 if(!channel
->banlist
.used
)
4412 reply("CSMSG_NO_BANS", channel
->name
);
4416 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4417 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4419 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4420 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4422 modcmd_chanmode_announce(change
);
4423 for(ii
= 0; ii
< change
->argc
; ++ii
)
4424 free((char*)change
->args
[ii
].u
.hostmask
);
4425 mod_chanmode_free(change
);
4426 reply("CSMSG_BANS_REMOVED", channel
->name
);
4430 static CHANSERV_FUNC(cmd_open
)
4432 struct mod_chanmode
*change
;
4435 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4437 change
= mod_chanmode_alloc(0);
4438 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4439 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4440 && channel
->channel_info
->modes
.modes_set
)
4441 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4442 modcmd_chanmode_announce(change
);
4443 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4444 for(ii
= 0; ii
< change
->argc
; ++ii
)
4445 free((char*)change
->args
[ii
].u
.hostmask
);
4446 mod_chanmode_free(change
);
4450 static CHANSERV_FUNC(cmd_myaccess
)
4452 static struct string_buffer sbuf
;
4453 struct handle_info
*target_handle
;
4454 struct userData
*uData
;
4457 target_handle
= user
->handle_info
;
4458 else if(!IsStaff(user
))
4460 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4463 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4466 if(!oper_outranks(user
, target_handle
))
4469 if(!target_handle
->channels
)
4471 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4475 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4476 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4478 struct chanData
*cData
= uData
->channel
;
4480 if(uData
->access
> UL_OWNER
)
4482 if(IsProtected(cData
)
4483 && (target_handle
!= user
->handle_info
)
4484 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4487 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4488 if(uData
->flags
!= 0)
4489 string_buffer_append(&sbuf
, ',');
4490 if(IsUserSuspended(uData
))
4491 string_buffer_append(&sbuf
, 's');
4492 if(IsUserAutoOp(uData
))
4494 if(uData
->access
>= UL_OP
)
4495 string_buffer_append(&sbuf
, 'o');
4496 else if(uData
->access
>= UL_HALFOP
)
4497 string_buffer_append(&sbuf
, 'h');
4498 else if(uData
->access
>= UL_PEON
)
4499 string_buffer_append(&sbuf
, 'v');
4501 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4502 string_buffer_append(&sbuf
, 'i');
4503 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4504 string_buffer_append(&sbuf
, 'j');
4506 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4508 string_buffer_append_string(&sbuf
, ")]");
4509 string_buffer_append(&sbuf
, '\0');
4510 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4516 static CHANSERV_FUNC(cmd_access
)
4518 struct userNode
*target
;
4519 struct handle_info
*target_handle
;
4520 struct userData
*uData
;
4522 char prefix
[MAXLEN
];
4527 target_handle
= target
->handle_info
;
4529 else if((target
= GetUserH(argv
[1])))
4531 target_handle
= target
->handle_info
;
4533 else if(argv
[1][0] == '*')
4535 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4537 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4543 reply("MSG_NICK_UNKNOWN", argv
[1]);
4547 assert(target
|| target_handle
);
4549 if(target
== chanserv
)
4551 reply("CSMSG_IS_CHANSERV");
4559 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4564 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4567 reply("MSG_AUTHENTICATE");
4573 const char *epithet
= NULL
, *type
= NULL
;
4576 epithet
= chanserv_conf
.irc_operator_epithet
;
4577 type
= user_find_message(user
, "CSMSG_OPERATOR_TITLE");
4579 else if(IsNetworkHelper(target
))
4581 epithet
= chanserv_conf
.network_helper_epithet
;
4582 type
= user_find_message(user
, "CSMSG_UC_H_TITLE");
4584 else if(IsSupportHelper(target
))
4586 epithet
= chanserv_conf
.support_helper_epithet
;
4587 type
= user_find_message(user
, "CSMSG_LC_H_TITLE");
4591 if(target_handle
->epithet
)
4592 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4594 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4596 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4600 sprintf(prefix
, "%s", target_handle
->handle
);
4603 if(!channel
->channel_info
)
4605 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4609 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4610 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4611 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4613 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4614 /* To prevent possible information leaks, only show infolines
4615 * if the requestor is in the channel or it's their own
4617 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4619 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4621 /* Likewise, only say it's suspended if the user has active
4622 * access in that channel or it's their own entry. */
4623 if(IsUserSuspended(uData
)
4624 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4625 || (user
->handle_info
== uData
->handle
)))
4627 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4632 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4638 /* This is never used...
4640 zoot_list(struct listData *list)
4642 struct userData *uData;
4643 unsigned int start, curr, highest, lowest;
4644 struct helpfile_table tmp_table;
4645 const char **temp, *msg;
4647 if(list->table.length == 1)
4650 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);
4652 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));
4653 msg = user_find_message(list->user, "MSG_NONE");
4654 send_message_type(4, list->user, list->bot, " %s", msg);
4656 tmp_table.width = list->table.width;
4657 tmp_table.flags = list->table.flags;
4658 list->table.contents[0][0] = " ";
4659 highest = list->highest;
4660 if(list->lowest != 0)
4661 lowest = list->lowest;
4662 else if(highest < 100)
4665 lowest = highest - 100;
4666 for(start = curr = 1; curr < list->table.length; )
4668 uData = list->users[curr-1];
4669 list->table.contents[curr++][0] = " ";
4670 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4673 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);
4675 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));
4676 temp = list->table.contents[--start];
4677 list->table.contents[start] = list->table.contents[0];
4678 tmp_table.contents = list->table.contents + start;
4679 tmp_table.length = curr - start;
4680 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4681 list->table.contents[start] = temp;
4683 highest = lowest - 1;
4684 lowest = (highest < 100) ? 0 : (highest - 99);
4691 normal_list(struct listData
*list
)
4695 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
);
4697 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
));
4698 if(list
->table
.length
== 1)
4700 msg
= user_find_message(list
->user
, "MSG_NONE");
4701 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4704 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4707 /* if these need changed, uncomment and customize
4709 clean_list(struct listData *list)
4713 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);
4715 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));
4716 if(list->table.length == 1)
4718 msg = user_find_message(list->user, "MSG_NONE");
4719 send_message_type(4, list->user, list->bot, " %s", msg);
4722 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4726 advanced_list(struct listData *list)
4730 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);
4732 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));
4733 if(list->table.length == 1)
4735 msg = user_find_message(list->user, "MSG_NONE");
4736 send_message_type(4, list->user, list->bot, " %s", msg);
4739 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4743 classic_list(struct listData *list)
4747 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4749 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4750 if(list->table.length == 1)
4752 msg = user_find_message(list->user, "MSG_NONE");
4753 send_message_type(4, list->user, list->bot, " %s", msg);
4756 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4761 userData_access_comp(const void *arg_a
, const void *arg_b
)
4763 const struct userData
*a
= *(struct userData
**)arg_a
;
4764 const struct userData
*b
= *(struct userData
**)arg_b
;
4766 if(a
->access
!= b
->access
)
4767 res
= b
->access
- a
->access
;
4769 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4774 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4776 void (*send_list
)(struct listData
*);
4777 struct userData
*uData
;
4778 struct listData lData
;
4779 unsigned int matches
;
4785 lData
.bot
= cmd
->parent
->bot
;
4786 lData
.channel
= channel
;
4787 lData
.lowest
= lowest
;
4788 lData
.highest
= highest
;
4789 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4790 send_list
= normal_list
;
4791 /* What does the following line do exactly?? */
4792 /*(void)zoot_list; ** since it doesn't show user levels */
4795 if(user->handle_info)
4797 switch(user->handle_info->userlist_style)
4799 case HI_STYLE_CLEAN:
4800 send_list = clean_list;
4802 case HI_STYLE_ADVANCED:
4803 send_list = advanced_list;
4805 case HI_STYLE_CLASSIC:
4806 send_list = classic_list;
4808 case HI_STYLE_NORMAL:
4810 send_list = normal_list;
4815 send_list
= normal_list
;
4817 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4819 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4821 if((uData
->access
< lowest
)
4822 || (uData
->access
> highest
)
4823 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4825 lData
.users
[matches
++] = uData
;
4827 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4829 lData
.table
.length
= matches
+1;
4830 lData
.table
.flags
= TABLE_NO_FREE
;
4831 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4833 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4834 lData
.table
.width
= 6; /* with level = 6 */
4836 lData
.table
.width
= 5; /* without = 5 */
4837 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4838 lData
.table
.contents
[0] = ary
;
4839 if(user
->handle_info
) {
4840 switch(user
->handle_info
->userlist_style
) {
4841 case HI_STYLE_CLASSIC
:
4844 case HI_STYLE_ADVANCED
:
4845 ary
[i
++] = "Access";
4848 case HI_STYLE_CLEAN
:
4849 ary
[i
++] = "Access";
4851 case HI_STYLE_NORMAL
:
4853 ary
[i
++] = "Access";
4858 ary
[i
++] = "Access";
4860 ary
[i
++] = "Account";
4861 ary
[i
] = "Last Seen";
4863 ary
[i
++] = "Status";
4864 ary
[i
++] = "Expiry";
4865 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4867 char seen
[INTERVALLEN
];
4870 uData
= lData
.users
[matches
-1];
4872 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4873 lData
.table
.contents
[matches
] = ary
;
4874 if(user
->handle_info
) {
4875 switch(user
->handle_info
->userlist_style
) {
4876 case HI_STYLE_CLASSIC
:
4877 ary
[i
++] = strtab(uData
->access
);
4879 case HI_STYLE_ADVANCED
:
4880 ary
[i
++] = user_level_name_from_level(uData
->access
);
4881 ary
[i
++] = strtab(uData
->access
);
4883 case HI_STYLE_CLEAN
:
4884 ary
[i
++] = user_level_name_from_level(uData
->access
);
4886 case HI_STYLE_NORMAL
:
4888 ary
[i
++] = user_level_name_from_level(uData
->access
);
4893 ary
[i
++] = user_level_name_from_level(uData
->access
);
4895 ary
[i
++] = uData
->handle
->handle
;
4898 else if(!uData
->seen
)
4901 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4902 ary
[i
] = strdup(ary
[i
]);
4904 if(IsUserSuspended(uData
))
4905 ary
[i
++] = "Suspended";
4906 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4907 ary
[i
++] = "Vacation";
4909 ary
[i
++] = "Normal";
4911 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4912 char delay
[INTERVALLEN
];
4915 if (uData
->accessexpiry
> 0) {
4916 diff
= uData
->accessexpiry
- now
;
4917 intervalString(delay
, diff
, user
->handle_info
);
4919 diff
= uData
->clvlexpiry
- now
;
4920 intervalString(delay
, diff
, user
->handle_info
);
4928 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4930 /* Free strdup above */
4931 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4932 free(lData
.table
.contents
[matches
]);
4934 free(lData
.table
.contents
[0]);
4935 free(lData
.table
.contents
);
4939 /* Remove this now that debugging is over? or improve it for
4940 * users? Would it be better tied into USERS somehow? -Rubin */
4941 static CHANSERV_FUNC(cmd_pending
)
4943 struct adduserPending
*ap
;
4944 reply("CSMSG_ADDUSER_PENDING_HEADER");
4945 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4947 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4948 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4949 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4953 static CHANSERV_FUNC(cmd_users
)
4955 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4958 static CHANSERV_FUNC(cmd_wlist
)
4960 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4963 static CHANSERV_FUNC(cmd_clist
)
4965 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4968 static CHANSERV_FUNC(cmd_mlist
)
4970 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4973 static CHANSERV_FUNC(cmd_olist
)
4975 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4978 static CHANSERV_FUNC(cmd_hlist
)
4980 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4983 static CHANSERV_FUNC(cmd_plist
)
4985 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4988 static CHANSERV_FUNC(cmd_lamers
)
4990 struct userNode
*search_u
= NULL
;
4991 struct helpfile_table tbl
;
4992 unsigned int matches
= 0, timed
= 0, search_wilds
= 0, ii
;
4993 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4994 const char *msg_never
, *triggered
, *expires
;
4995 struct banData
*ban
, **bans
; /* lamers */
4999 else if(strchr(search
= argv
[1], '!'))
5002 search_wilds
= search
[strcspn(search
, "?*")];
5004 else if(!(search_u
= GetUserH(search
)))
5005 reply("MSG_NICK_UNKNOWN", search
);
5007 reply("CSMSG_LAMERS_HEADER", channel
->name
);
5008 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
5011 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
5015 if(!user_matches_glob(search_u
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
5020 if(search_wilds
? !match_ircglobs(search
, ban
->mask
) : !match_ircglob(search
, ban
->mask
))
5023 bans
[matches
++] = ban
;
5028 tbl
.length
= matches
+ 1;
5029 tbl
.width
= 4 + timed
;
5031 tbl
.flags
= TABLE_NO_FREE
;
5032 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
5033 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5034 tbl
.contents
[0][0] = "Mask";
5035 tbl
.contents
[0][1] = "Set By";
5036 tbl
.contents
[0][2] = "Triggered";
5039 tbl
.contents
[0][3] = "Expires";
5040 tbl
.contents
[0][4] = "Reason";
5043 tbl
.contents
[0][3] = "Reason";
5046 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5047 /* reply("MSG_NONE"); */
5048 free(tbl
.contents
[0]);
5053 msg_never
= user_find_message(user
, "MSG_NEVER");
5054 for(ii
= 0; ii
< matches
; )
5060 else if(ban
->expires
)
5061 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
5063 expires
= msg_never
;
5066 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
5068 triggered
= msg_never
;
5070 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5071 tbl
.contents
[ii
][0] = ban
->mask
;
5072 tbl
.contents
[ii
][1] = ban
->owner
;
5073 tbl
.contents
[ii
][2] = strdup(triggered
);
5076 tbl
.contents
[ii
][3] = strdup(expires
);
5077 tbl
.contents
[ii
][4] = ban
->reason
;
5080 tbl
.contents
[ii
][3] = ban
->reason
;
5082 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5083 /* reply("MSG_MATCH_COUNT", matches); */
5084 for(ii
= 1; ii
< tbl
.length
; ++ii
)
5086 free((char*)tbl
.contents
[ii
][2]);
5088 free((char*)tbl
.contents
[ii
][3]);
5089 free(tbl
.contents
[ii
]);
5091 free(tbl
.contents
[0]);
5098 * return + if the user does NOT have the right to set the topic, and
5099 * the topic is changed.
5102 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
5104 struct chanData
*cData
= channel
->channel_info
;
5105 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5107 else if(cData
->topic
)
5108 return irccasecmp(new_topic
, cData
->topic
);
5115 * Makes a givin topic fit into a givin topic mask and returns
5118 * topic_mask - the mask to conform to
5119 * topic - the topic to make conform
5120 * new_topic - the pre-allocated char* to put the new topic into
5122 * modifies: new_topic
5125 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
5127 //char *topic_mask = cData->topic_mask;
5129 int pos
=0, starpos
=-1, dpos
=0, len
;
5131 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
5138 strcpy(new_topic
, "");
5141 len
= strlen(topic
);
5142 if((dpos
+ len
) > TOPICLEN
)
5143 len
= TOPICLEN
+ 1 - dpos
;
5144 memcpy(new_topic
+dpos
, topic
, len
);
5148 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
5149 default: new_topic
[dpos
++] = tchar
; break;
5152 if((dpos
> TOPICLEN
) || tchar
)
5154 strcpy(new_topic
, "");
5157 new_topic
[dpos
] = 0;
5161 static CHANSERV_FUNC(cmd_topic
)
5163 struct chanData
*cData
;
5167 #ifdef WITH_PROTOCOL_P10
5171 cData
= channel
->channel_info
;
5176 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5177 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
5178 reply("CSMSG_TOPIC_SET", cData
->topic
);
5182 reply("CSMSG_NO_TOPIC", channel
->name
);
5186 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5187 /* If they say "!topic *", use an empty topic. */
5188 if((topic
[0] == '*') && (topic
[1] == 0))
5191 if(bad_topic(channel
, user
, topic
))
5193 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5198 /* If there is a topicmask set, and the new topic doesnt match, make it */
5199 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
5201 char *topic_mask
= cData
->topic_mask
;
5202 char new_topic
[TOPICLEN
+1];
5204 /* make a new topic fitting mask */
5205 conform_topic(topic_mask
, topic
, new_topic
);
5208 /* Topic couldnt fit into mask, was too long */
5209 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
5210 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
5213 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
5215 else /* No mask set, just set the topic */
5216 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
5219 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
5221 /* Grab the topic and save it as the default topic. */
5223 cData
->topic
= strdup(channel
->topic
);
5229 static CHANSERV_FUNC(cmd_mode
)
5231 struct userData
*uData
;
5232 struct mod_chanmode
*change
;
5237 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5238 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5242 change
= &channel
->channel_info
->modes
;
5243 if(change
->modes_set
|| change
->modes_clear
) {
5244 modcmd_chanmode_announce(change
);
5245 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
5247 reply("CSMSG_NO_MODES", channel
->name
);
5251 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5253 base_oplevel
= MAXOPLEVEL
;
5254 else if (uData
->access
>= UL_OWNER
)
5257 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
5258 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
5262 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
5266 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
5267 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
5270 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5271 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
5275 modcmd_chanmode_announce(change
);
5276 mod_chanmode_free(change
);
5277 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
5281 static CHANSERV_FUNC(cmd_invite
)
5283 struct userData
*uData
;
5284 struct userNode
*invite
;
5286 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5290 if(!(invite
= GetUserH(argv
[1])))
5292 reply("MSG_NICK_UNKNOWN", argv
[1]);
5299 if(GetUserMode(channel
, invite
))
5301 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
5309 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5310 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
5313 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
5316 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
5318 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
5319 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
, 0)) {
5320 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
5326 irc_invite(chanserv
, invite
, channel
);
5328 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
5333 static CHANSERV_FUNC(cmd_inviteme
)
5335 if(GetUserMode(channel
, user
))
5337 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
5340 if(channel
->channel_info
5341 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
5343 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
5346 irc_invite(cmd
->parent
->bot
, user
, channel
);
5351 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
5354 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
5356 /* We display things based on two dimensions:
5357 * - Issue time: present or absent
5358 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5359 * (in order of precedence, so something both expired and revoked
5360 * only counts as revoked)
5362 combo
= (suspended
->issued
? 4 : 0)
5363 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
5365 case 0: /* no issue time, indefinite expiration */
5366 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
5368 case 1: /* no issue time, expires in future */
5369 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
5370 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
5372 case 2: /* no issue time, expired */
5373 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
5374 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
5376 case 3: /* no issue time, revoked */
5377 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
5378 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
5380 case 4: /* issue time set, indefinite expiration */
5381 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5382 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
5384 case 5: /* issue time set, expires in future */
5385 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5386 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
5387 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5389 case 6: /* issue time set, expired */
5390 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5391 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
5392 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5394 case 7: /* issue time set, revoked */
5395 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5396 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
5397 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5400 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
5406 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
5409 const char *fmt
= "%a %b %d %H:%M %Y";
5410 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
5412 if(giveownership
->staff_issuer
)
5414 if(giveownership
->reason
)
5415 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
5416 giveownership
->target
, giveownership
->target_access
,
5417 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
5419 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
5420 giveownership
->target
, giveownership
->target_access
,
5421 giveownership
->staff_issuer
, buf
);
5425 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5430 static CHANSERV_FUNC(cmd_info
)
5432 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5433 struct userData
*uData
, *owner
;
5434 struct chanData
*cData
;
5435 struct do_not_register
*dnr
;
5440 cData
= channel
->channel_info
;
5441 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5442 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5445 uData
= GetChannelUser(cData
, user
->handle_info
);
5446 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5448 mod_chanmode_format(&cData
->modes
, modes
);
5449 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5450 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5453 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5457 note
= iter_data(it
);
5458 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5461 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5462 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5465 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5466 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5467 if(owner
->access
== UL_OWNER
)
5468 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5469 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5470 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5471 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5473 privileged
= IsStaff(user
);
5474 /* if(privileged) */
5475 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5476 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5477 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5479 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5480 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5482 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5484 struct suspended
*suspended
;
5485 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5486 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5487 show_suspension_info(cmd
, user
, suspended
);
5489 else if(IsSuspended(cData
))
5491 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5492 show_suspension_info(cmd
, user
, cData
->suspended
);
5494 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5496 struct giveownership
*giveownership
;
5497 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5498 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5499 show_giveownership_info(cmd
, user
, giveownership
);
5501 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5502 reply("CSMSG_CHANNEL_END");
5504 reply("CSMSG_CHANNEL_END_CLEAN");
5508 static CHANSERV_FUNC(cmd_netinfo
)
5510 extern time_t boot_time
;
5511 extern unsigned long burst_length
;
5512 char interval
[INTERVALLEN
];
5514 reply("CSMSG_NETWORK_INFO");
5515 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5516 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5517 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5518 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5519 reply("CSMSG_NETWORK_LAMERS", banCount
);
5520 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5521 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5522 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5527 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5529 struct helpfile_table table
;
5531 struct userNode
*user
;
5536 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5537 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5538 for(nn
=0; nn
<list
->used
; nn
++)
5540 user
= list
->list
[nn
];
5541 if(user
->modes
& skip_flags
)
5545 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5548 nick
= alloca(strlen(user
->nick
)+3);
5549 sprintf(nick
, "(%s)", user
->nick
);
5553 table
.contents
[table
.length
][0] = nick
;
5556 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5559 static CHANSERV_FUNC(cmd_ircops
)
5561 reply("CSMSG_STAFF_OPERS");
5562 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5566 static CHANSERV_FUNC(cmd_helpers
)
5568 reply("CSMSG_STAFF_HELPERS");
5569 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5573 static CHANSERV_FUNC(cmd_staff
)
5575 reply("CSMSG_NETWORK_STAFF");
5576 cmd_ircops(CSFUNC_ARGS
);
5577 cmd_helpers(CSFUNC_ARGS
);
5581 static CHANSERV_FUNC(cmd_peek
)
5583 struct modeNode
*mn
;
5584 char modes
[MODELEN
];
5586 struct helpfile_table table
;
5588 irc_make_chanmode(channel
, modes
);
5590 reply("CSMSG_PEEK_INFO", channel
->name
);
5591 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5593 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5594 reply("CSMSG_PEEK_MODES", modes
);
5595 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5599 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5600 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5601 for(n
= 0; n
< channel
->members
.used
; n
++)
5603 mn
= channel
->members
.list
[n
];
5604 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5606 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5607 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5612 reply("CSMSG_PEEK_OPS");
5613 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5616 reply("CSMSG_PEEK_NO_OPS");
5617 reply("CSMSG_PEEK_END");
5621 static MODCMD_FUNC(cmd_wipeinfo
)
5623 struct handle_info
*victim
;
5624 struct userData
*ud
, *actor
, *real_actor
;
5625 unsigned int override
= 0;
5628 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5629 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5630 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5632 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5634 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5637 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5639 reply("MSG_USER_OUTRANKED", victim
->handle
);
5642 if((ud
!= real_actor
) && (!real_actor
|| (ud
->access
>= real_actor
->access
)))
5643 override
= CMD_LOG_OVERRIDE
;
5647 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5648 return 1 | override
;
5652 resync_channel(struct chanNode
*channel
)
5654 struct mod_chanmode
*changes
;
5655 struct chanData
*cData
= channel
->channel_info
;
5656 unsigned int ii
, used
;
5658 /* 6 = worst case -ovh+ovh on everyone */
5659 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5660 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5662 struct modeNode
*mn
= channel
->members
.list
[ii
];
5663 struct userData
*uData
;
5665 if(IsService(mn
->user
))
5669 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5671 /* If the channel is in no-mode mode, de-mode EVERYONE */
5672 if(cData
->chOpts
[chAutomode
] == 'n')
5676 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5677 changes
->args
[used
++].u
.member
= mn
;
5680 else /* Give various userlevels their modes.. */
5682 if(uData
&& uData
->access
>= UL_OP
)
5684 if(!(mn
->modes
& MODE_CHANOP
))
5686 changes
->args
[used
].mode
= MODE_CHANOP
;
5687 changes
->args
[used
++].u
.member
= mn
;
5690 else if(uData
&& uData
->access
>= UL_HALFOP
)
5692 if(mn
->modes
& MODE_CHANOP
)
5694 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5695 changes
->args
[used
++].u
.member
= mn
;
5697 if(!(mn
->modes
& MODE_HALFOP
))
5699 changes
->args
[used
].mode
= MODE_HALFOP
;
5700 changes
->args
[used
++].u
.member
= mn
;
5703 else if(uData
&& uData
->access
>= UL_PEON
)
5705 if(mn
->modes
& MODE_CHANOP
)
5707 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5708 changes
->args
[used
++].u
.member
= mn
;
5710 if(mn
->modes
& MODE_HALFOP
)
5712 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5713 changes
->args
[used
++].u
.member
= mn
;
5715 /* Don't voice peons if were in mode m */
5716 if( cData
->chOpts
[chAutomode
] == 'm')
5718 if(mn
->modes
& MODE_VOICE
)
5720 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5721 changes
->args
[used
++].u
.member
= mn
;
5724 /* otherwise, make user they do have voice */
5725 else if(!(mn
->modes
& MODE_VOICE
))
5727 changes
->args
[used
].mode
= MODE_VOICE
;
5728 changes
->args
[used
++].u
.member
= mn
;
5731 else /* They arnt on the userlist.. */
5733 /* If we voice everyone, but they dont.. */
5734 if(cData
->chOpts
[chAutomode
] == 'v')
5736 /* Remove anything except v */
5737 if(mn
->modes
& ~MODE_VOICE
)
5739 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5740 changes
->args
[used
++].u
.member
= mn
;
5743 if(!(mn
->modes
& MODE_VOICE
))
5745 changes
->args
[used
].mode
= MODE_VOICE
;
5746 changes
->args
[used
++].u
.member
= mn
;
5749 /* If we hop everyone, but they dont.. */
5750 else if(cData
->chOpts
[chAutomode
] == 'h')
5752 /* Remove anything except h */
5753 if(mn
->modes
& ~MODE_HALFOP
)
5755 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5756 changes
->args
[used
++].u
.member
= mn
;
5759 if(!(mn
->modes
& MODE_HALFOP
))
5761 changes
->args
[used
].mode
= MODE_HALFOP
;
5762 changes
->args
[used
++].u
.member
= mn
;
5765 /* If we op everyone, but they dont.. */
5766 else if(cData
->chOpts
[chAutomode
] == 'o')
5768 /* Remove anything except h */
5769 if(mn
->modes
& ~MODE_CHANOP
)
5771 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5772 changes
->args
[used
++].u
.member
= mn
;
5775 if(!(mn
->modes
& MODE_CHANOP
))
5777 changes
->args
[used
].mode
= MODE_CHANOP
;
5778 changes
->args
[used
++].u
.member
= mn
;
5781 /* they have no excuse for having modes, de-everything them */
5786 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5787 changes
->args
[used
++].u
.member
= mn
;
5793 changes
->argc
= used
;
5794 mod_chanmode_announce(chanserv
, channel
, changes
);
5795 mod_chanmode_free(changes
);
5798 static CHANSERV_FUNC(cmd_resync
)
5800 resync_channel(channel
);
5801 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5805 static CHANSERV_FUNC(cmd_seen
)
5807 struct userData
*uData
;
5808 struct handle_info
*handle
;
5809 char seen
[INTERVALLEN
];
5813 if(!irccasecmp(argv
[1], chanserv
->nick
))
5815 reply("CSMSG_IS_CHANSERV");
5819 if(!(handle
= get_handle_info(argv
[1])))
5821 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5825 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5827 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5832 reply("CSMSG_USER_PRESENT", handle
->handle
);
5833 else if(uData
->seen
)
5834 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5836 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5838 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5839 reply("CSMSG_USER_VACATION", handle
->handle
);
5844 static MODCMD_FUNC(cmd_names
)
5846 struct userNode
*targ
;
5847 struct userData
*targData
;
5848 unsigned int ii
, pos
;
5851 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5853 targ
= channel
->members
.list
[ii
]->user
;
5854 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5857 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5860 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5864 if(IsUserSuspended(targData
))
5866 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5869 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5870 reply("CSMSG_END_NAMES", channel
->name
);
5875 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5877 switch(ntype
->visible_type
)
5879 case NOTE_VIS_ALL
: return 1;
5880 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5881 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5886 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5888 struct userData
*uData
;
5890 switch(ntype
->set_access_type
)
5892 case NOTE_SET_CHANNEL_ACCESS
:
5893 if(!user
->handle_info
)
5895 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5897 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5898 case NOTE_SET_CHANNEL_SETTER
:
5899 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5900 case NOTE_SET_PRIVILEGED
: default:
5901 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5905 static CHANSERV_FUNC(cmd_note
)
5907 struct chanData
*cData
;
5909 struct note_type
*ntype
;
5911 cData
= channel
->channel_info
;
5914 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5918 /* If no arguments, show all visible notes for the channel. */
5924 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5926 note
= iter_data(it
);
5927 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5930 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5931 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5934 reply("CSMSG_NOTELIST_END", channel
->name
);
5936 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5938 /* If one argument, show the named note. */
5941 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5942 && note_type_visible_to_user(cData
, note
->type
, user
))
5944 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5946 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5947 && note_type_visible_to_user(NULL
, ntype
, user
))
5949 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5954 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5958 /* Assume they're trying to set a note. */
5962 ntype
= dict_find(note_types
, argv
[1], NULL
);
5965 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5968 else if(note_type_settable_by_user(channel
, ntype
, user
))
5970 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5971 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5972 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5973 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5974 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5976 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5978 /* The note is viewable to staff only, so return 0
5979 to keep the invocation from getting logged (or
5980 regular users can see it in !events). */
5986 reply("CSMSG_NO_ACCESS");
5993 static CHANSERV_FUNC(cmd_delnote
)
5998 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5999 || !note_type_settable_by_user(channel
, note
->type
, user
))
6001 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
6004 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
6005 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
6009 static CHANSERV_FUNC(cmd_last
)
6015 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
6017 if(numoflines
< 1 || numoflines
> 200)
6019 reply("CSMSG_LAST_INVALID");
6022 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
6026 static CHANSERV_FUNC(cmd_events
)
6028 struct logSearch discrim
;
6029 struct logReport report
;
6030 unsigned int matches
, limit
;
6032 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
6033 if(limit
< 1 || limit
> 200)
6036 memset(&discrim
, 0, sizeof(discrim
));
6037 discrim
.masks
.bot
= chanserv
;
6038 discrim
.masks
.channel_name
= channel
->name
;
6040 discrim
.masks
.command
= argv
[2];
6041 discrim
.limit
= limit
;
6042 discrim
.max_time
= INT_MAX
;
6043 discrim
.severities
= 1 << LOG_COMMAND
;
6044 report
.reporter
= chanserv
;
6046 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
6047 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6049 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
6051 reply("MSG_MATCH_COUNT", matches
);
6053 reply("MSG_NO_MATCHES");
6057 static CHANSERV_FUNC(cmd_say
)
6063 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6064 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
6066 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6068 struct handle_info
*hi
;
6069 struct userNode
*authed
;
6072 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6074 if (!(hi
= get_handle_info(argv
[1] + 1)))
6076 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6080 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6081 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "%s", msg
);
6083 else if(GetUserH(argv
[1]))
6086 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6087 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
6091 reply("MSG_NOT_TARGET_NAME");
6097 static CHANSERV_FUNC(cmd_emote
)
6103 /* CTCP is so annoying. */
6104 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6105 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6107 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6109 struct handle_info
*hi
;
6110 struct userNode
*authed
;
6113 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6115 if (!(hi
= get_handle_info(argv
[1] + 1)))
6117 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6121 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6122 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6124 else if(GetUserH(argv
[1]))
6126 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6127 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6131 reply("MSG_NOT_TARGET_NAME");
6137 struct channelList
*
6138 chanserv_support_channels(void)
6140 return &chanserv_conf
.support_channels
;
6143 static CHANSERV_FUNC(cmd_expire
)
6145 int channel_count
= registered_channels
;
6146 expire_channels(NULL
);
6147 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
6152 chanserv_expire_suspension(void *data
)
6154 struct suspended
*suspended
= data
;
6155 struct chanNode
*channel
;
6158 /* Update the channel registration data structure. */
6159 if(!suspended
->expires
|| (now
< suspended
->expires
))
6160 suspended
->revoked
= now
;
6161 channel
= suspended
->cData
->channel
;
6162 suspended
->cData
->channel
= channel
;
6163 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
6165 /* If appropriate, re-join ChanServ to the channel. */
6166 if(!IsOffChannel(suspended
->cData
))
6168 spamserv_cs_suspend(channel
, 0, 0, NULL
);
6169 ss_cs_join_channel(channel
, 1);
6172 /* Mark everyone currently in the channel as present. */
6173 for(ii
= 0; ii
< channel
->members
.used
; ++ii
)
6175 struct userData
*uData
= GetChannelAccess(suspended
->cData
, channel
->members
.list
[ii
]->user
->handle_info
);
6184 static CHANSERV_FUNC(cmd_csuspend
)
6186 struct suspended
*suspended
;
6187 char reason
[MAXLEN
];
6188 time_t expiry
, duration
;
6189 struct userData
*uData
;
6193 if(IsProtected(channel
->channel_info
))
6195 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
6199 if(argv
[1][0] == '!')
6201 else if(IsSuspended(channel
->channel_info
))
6203 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
6204 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
6208 if(!strcmp(argv
[1], "0"))
6210 else if((duration
= ParseInterval(argv
[1])))
6211 expiry
= now
+ duration
;
6214 reply("MSG_INVALID_DURATION", argv
[1]);
6218 unsplit_string(argv
+ 2, argc
- 2, reason
);
6220 suspended
= calloc(1, sizeof(*suspended
));
6221 suspended
->revoked
= 0;
6222 suspended
->issued
= now
;
6223 suspended
->suspender
= strdup(user
->handle_info
->handle
);
6224 suspended
->expires
= expiry
;
6225 suspended
->reason
= strdup(reason
);
6226 suspended
->cData
= channel
->channel_info
;
6227 suspended
->previous
= suspended
->cData
->suspended
;
6228 suspended
->cData
->suspended
= suspended
;
6230 if(suspended
->expires
)
6231 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6233 if(IsSuspended(channel
->channel_info
))
6235 suspended
->previous
->revoked
= now
;
6236 if(suspended
->previous
->expires
)
6237 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
6239 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
6240 channel
->name
, suspended
->suspender
);
6244 /* Mark all users in channel as absent. */
6245 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
6254 /* Mark the channel as suspended, then part. */
6255 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
6256 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
6257 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
6258 reply("CSMSG_SUSPENDED", channel
->name
);
6259 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
6260 channel
->name
, suspended
->suspender
);
6265 static CHANSERV_FUNC(cmd_cunsuspend
)
6267 struct suspended
*suspended
;
6269 if(!IsSuspended(channel
->channel_info
))
6271 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
6275 suspended
= channel
->channel_info
->suspended
;
6277 /* Expire the suspension and join ChanServ to the channel. */
6278 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
6279 chanserv_expire_suspension(suspended
);
6280 reply("CSMSG_UNSUSPENDED", channel
->name
);
6281 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
6282 channel
->name
, user
->handle_info
->handle
);
6286 typedef struct chanservSearch
6294 unsigned long flags
;
6298 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
6301 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
6306 search
= malloc(sizeof(struct chanservSearch
));
6307 memset(search
, 0, sizeof(*search
));
6310 for(i
= 0; i
< argc
; i
++)
6312 /* Assume all criteria require arguments. */
6315 reply("MSG_MISSING_PARAMS", argv
[i
]);
6319 if(!irccasecmp(argv
[i
], "name"))
6320 search
->name
= argv
[++i
];
6321 else if(!irccasecmp(argv
[i
], "registrar"))
6322 search
->registrar
= argv
[++i
];
6323 else if(!irccasecmp(argv
[i
], "unvisited"))
6324 search
->unvisited
= ParseInterval(argv
[++i
]);
6325 else if(!irccasecmp(argv
[i
], "registered"))
6326 search
->registered
= ParseInterval(argv
[++i
]);
6327 else if(!irccasecmp(argv
[i
], "flags"))
6330 if(!irccasecmp(argv
[i
], "nodelete"))
6331 search
->flags
|= CHANNEL_NODELETE
;
6332 else if(!irccasecmp(argv
[i
], "suspended"))
6333 search
->flags
|= CHANNEL_SUSPENDED
;
6334 else if(!irccasecmp(argv
[i
], "unreviewed"))
6335 search
->flags
|= CHANNEL_UNREVIEWED
;
6338 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
6342 else if(!irccasecmp(argv
[i
], "limit"))
6343 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
6346 reply("MSG_INVALID_CRITERIA", argv
[i
]);
6351 if(search
->name
&& !strcmp(search
->name
, "*"))
6353 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
6354 search
->registrar
= 0;
6363 chanserv_channel_match(struct chanData
*channel
, search_t search
)
6365 const char *name
= channel
->channel
->name
;
6366 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
6367 (search
->registrar
&& !channel
->registrar
) ||
6368 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
6369 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
6370 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
6371 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
6378 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
6380 struct chanData
*channel
;
6381 unsigned int matches
= 0;
6383 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
6385 if(!chanserv_channel_match(channel
, search
))
6395 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
6400 search_print(struct chanData
*channel
, void *data
)
6402 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
6405 static CHANSERV_FUNC(cmd_search
)
6408 unsigned int matches
;
6409 channel_search_func action
;
6413 if(!irccasecmp(argv
[1], "count"))
6414 action
= search_count
;
6415 else if(!irccasecmp(argv
[1], "print"))
6416 action
= search_print
;
6419 reply("CSMSG_ACTION_INVALID", argv
[1]);
6423 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
6427 if(action
== search_count
)
6428 search
->limit
= INT_MAX
;
6430 if(action
== search_print
)
6432 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6433 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6437 matches
= chanserv_channel_search(search
, action
, user
);
6440 reply("MSG_MATCH_COUNT", matches
);
6442 reply("MSG_NO_MATCHES");
6448 static CHANSERV_FUNC(cmd_unvisited
)
6450 struct chanData
*cData
;
6451 time_t interval
= chanserv_conf
.channel_expire_delay
;
6452 char buffer
[INTERVALLEN
];
6453 unsigned int limit
= 25, matches
= 0;
6457 interval
= ParseInterval(argv
[1]);
6459 limit
= atoi(argv
[2]);
6462 intervalString(buffer
, interval
, user
->handle_info
);
6463 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
6465 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
6467 if((now
- cData
->visited
) < interval
)
6470 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
6471 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
6478 static MODCMD_FUNC(chan_opt_unreviewed
)
6480 struct chanData
*cData
= channel
->channel_info
;
6481 int value
= (cData
->flags
& CHANNEL_UNREVIEWED
) ? 1 : 0;
6487 /* The two directions can have different ACLs. */
6488 if(enabled_string(argv
[1]))
6490 else if(disabled_string(argv
[1]))
6494 reply("MSG_INVALID_BINARY", argv
[1]);
6498 if (new_value
!= value
)
6500 struct svccmd
*subcmd
;
6501 char subcmd_name
[32];
6503 snprintf(subcmd_name
, sizeof(subcmd_name
), "%s %s", argv
[0], (new_value
? "on" : "off"));
6504 subcmd
= dict_find(cmd
->parent
->commands
, subcmd_name
, NULL
);
6507 reply("MSG_COMMAND_DISABLED", subcmd_name
);
6510 else if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
6514 cData
->flags
|= CHANNEL_UNREVIEWED
;
6517 free(cData
->registrar
);
6518 cData
->registrar
= strdup(user
->handle_info
->handle
);
6519 cData
->flags
&= ~CHANNEL_UNREVIEWED
;
6526 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_ON"));
6528 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_OFF"));
6532 static MODCMD_FUNC(chan_opt_defaulttopic
)
6538 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6540 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6544 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6546 free(channel
->channel_info
->topic
);
6547 if(topic
[0] == '*' && topic
[1] == 0)
6549 topic
= channel
->channel_info
->topic
= NULL
;
6553 topic
= channel
->channel_info
->topic
= strdup(topic
);
6554 if(channel
->channel_info
->topic_mask
6555 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6556 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6558 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6561 if(channel
->channel_info
->topic
)
6562 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6564 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6568 static MODCMD_FUNC(chan_opt_topicmask
)
6572 struct chanData
*cData
= channel
->channel_info
;
6575 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6577 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6581 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6583 if(cData
->topic_mask
)
6584 free(cData
->topic_mask
);
6585 if(mask
[0] == '*' && mask
[1] == 0)
6587 cData
->topic_mask
= 0;
6591 cData
->topic_mask
= strdup(mask
);
6593 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6594 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6595 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6599 if(channel
->channel_info
->topic_mask
)
6600 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6602 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6606 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6610 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6614 if(greeting
[0] == '*' && greeting
[1] == 0)
6618 unsigned int length
= strlen(greeting
);
6619 if(length
> chanserv_conf
.greeting_length
)
6621 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6624 *data
= strdup(greeting
);
6633 reply(name
, user_find_message(user
, "MSG_NONE"));
6637 static MODCMD_FUNC(chan_opt_greeting
)
6639 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6642 static MODCMD_FUNC(chan_opt_usergreeting
)
6644 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6647 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6649 unsigned int charmax
;
6652 charmax
= atoi(argv
[1]);
6653 if ((charmax
> 0) && (charmax
< chanserv_conf
.max_userinfo_length
))
6654 channel
->channel_info
->maxsetinfo
= charmax
;
6657 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6661 static MODCMD_FUNC(chan_opt_modes
)
6663 struct mod_chanmode
*new_modes
;
6664 char modes
[MODELEN
];
6668 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6669 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6673 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6675 reply("CSMSG_NO_ACCESS");
6678 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6680 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6682 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6684 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6687 else if(new_modes
->argc
> 1)
6689 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6690 mod_chanmode_free(new_modes
);
6695 channel
->channel_info
->modes
= *new_modes
;
6696 modcmd_chanmode_announce(new_modes
);
6697 mod_chanmode_free(new_modes
);
6701 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6703 reply("CSMSG_SET_MODES", modes
);
6705 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6709 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6711 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6713 struct chanData
*cData
= channel
->channel_info
;
6718 /* Set flag according to value. */
6719 if(enabled_string(argv
[1]))
6721 cData
->flags
|= mask
;
6724 else if(disabled_string(argv
[1]))
6726 cData
->flags
&= ~mask
;
6731 reply("MSG_INVALID_BINARY", argv
[1]);
6737 /* Find current option value. */
6738 value
= (cData
->flags
& mask
) ? 1 : 0;
6742 reply(name
, user_find_message(user
, "MSG_ON"));
6744 reply(name
, user_find_message(user
, "MSG_OFF"));
6748 static MODCMD_FUNC(chan_opt_nodelete
)
6750 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6752 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6756 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6759 static MODCMD_FUNC(chan_opt_dynlimit
)
6761 struct mod_chanmode change
;
6764 if (disabled_string(argv
[1])) {
6765 mod_chanmode_init(&change
);
6766 change
.modes_clear
|= MODE_LIMIT
;
6767 mod_chanmode_announce(chanserv
, channel
, &change
);
6771 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6774 static MODCMD_FUNC(chan_opt_offchannel
)
6776 struct chanData
*cData
= channel
->channel_info
;
6781 /* Set flag according to value. */
6782 if(enabled_string(argv
[1]))
6784 if(!IsOffChannel(cData
))
6785 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6786 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6789 else if(disabled_string(argv
[1]))
6791 if(IsOffChannel(cData
))
6793 struct mod_chanmode change
;
6794 mod_chanmode_init(&change
);
6796 change
.args
[0].mode
= MODE_CHANOP
;
6797 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6798 mod_chanmode_announce(chanserv
, channel
, &change
);
6800 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6805 reply("MSG_INVALID_BINARY", argv
[1]);
6811 /* Find current option value. */
6812 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6816 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6818 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6822 static MODCMD_FUNC(chan_opt_defaults
)
6824 struct userData
*uData
;
6825 struct chanData
*cData
;
6826 const char *confirm
;
6827 enum levelOption lvlOpt
;
6828 enum charOption chOpt
;
6830 cData
= channel
->channel_info
;
6831 uData
= GetChannelUser(cData
, user
->handle_info
);
6832 if(!uData
|| (uData
->access
< UL_OWNER
))
6834 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6837 confirm
= make_confirmation_string(uData
);
6838 if((argc
< 2) || strcmp(argv
[1], confirm
))
6840 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6843 cData
->flags
= (CHANNEL_DEFAULT_FLAGS
& ~CHANNEL_PRESERVED_FLAGS
)
6844 | (cData
->flags
& CHANNEL_PRESERVED_FLAGS
);
6845 cData
->modes
= chanserv_conf
.default_modes
;
6846 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6847 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6848 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6849 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6850 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6855 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6857 struct chanData
*cData
= channel
->channel_info
;
6858 struct userData
*uData
;
6859 unsigned short value
;
6863 if(!check_user_level(channel
, user
, option
, 1, 1))
6865 reply("CSMSG_CANNOT_SET");
6868 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6869 if(!value
&& strcmp(argv
[1], "0"))
6871 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6874 uData
= GetChannelUser(cData
, user
->handle_info
);
6875 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6877 reply("CSMSG_BAD_SETLEVEL");
6883 /* This test only applies to owners, since non-owners
6884 * trying to set an option to above their level get caught
6885 * by the CSMSG_BAD_SETLEVEL test above.
6887 if(value
> uData
->access
)
6889 reply("CSMSG_BAD_SETTERS");
6896 cData
->lvlOpts
[option
] = value
;
6898 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6902 static MODCMD_FUNC(chan_opt_enfops
)
6904 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6907 static MODCMD_FUNC(chan_opt_enfhalfops
)
6909 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6911 static MODCMD_FUNC(chan_opt_enfmodes
)
6913 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6916 static MODCMD_FUNC(chan_opt_enftopic
)
6918 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6921 static MODCMD_FUNC(chan_opt_pubcmd
)
6923 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6926 static MODCMD_FUNC(chan_opt_setters
)
6928 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6931 static MODCMD_FUNC(chan_opt_userinfo
)
6933 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6936 static MODCMD_FUNC(chan_opt_topicsnarf
)
6938 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6941 static MODCMD_FUNC(chan_opt_inviteme
)
6943 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6946 /* TODO: Make look like this when no args are
6948 * -X3- -------------------------------
6949 * -X3- BanTimeout: Bans are removed:
6950 * -X3- ----- * indicates current -----
6951 * -X3- 0: [*] Never.
6952 * -X3- 1: [ ] After 10 minutes.
6953 * -X3- 2: [ ] After 2 hours.
6954 * -X3- 3: [ ] After 4 hours.
6955 * -X3- 4: [ ] After 24 hours.
6956 * -X3- 5: [ ] After one week.
6957 * -X3- ------------- End -------------
6960 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6962 struct chanData
*cData
= channel
->channel_info
;
6963 int count
= charOptions
[option
].count
, idx
;
6967 idx
= atoi(argv
[1]);
6969 if(!isdigit(argv
[1][0]) || (idx
< 0) || (idx
>= count
))
6971 reply("CSMSG_INVALID_NUMERIC", idx
);
6972 /* Show possible values. */
6973 for(idx
= 0; idx
< count
; idx
++)
6974 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
6978 cData
->chOpts
[option
] = charOptions
[option
].values
[idx
].value
;
6982 /* Find current option value. */
6985 (idx
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[idx
].value
);
6989 /* Somehow, the option value is corrupt; reset it to the default. */
6990 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6995 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
6999 static MODCMD_FUNC(chan_opt_automode
)
7001 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
7004 static MODCMD_FUNC(chan_opt_protect
)
7006 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
7009 static MODCMD_FUNC(chan_opt_toys
)
7011 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
7014 static MODCMD_FUNC(chan_opt_ctcpreaction
)
7016 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
7019 static MODCMD_FUNC(chan_opt_bantimeout
)
7021 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
7024 static MODCMD_FUNC(chan_opt_topicrefresh
)
7026 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
7029 static MODCMD_FUNC(chan_opt_resync
)
7031 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
7034 static struct svccmd_list set_shows_list
;
7037 handle_svccmd_unbind(struct svccmd
*target
, UNUSED_ARG(void *extra
)) {
7039 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
7040 if(target
== set_shows_list
.list
[ii
])
7041 set_shows_list
.used
= 0;
7044 static CHANSERV_FUNC(cmd_set
)
7046 struct svccmd
*subcmd
;
7050 /* Check if we need to (re-)initialize set_shows_list. */
7051 if(!set_shows_list
.used
)
7053 if(!set_shows_list
.size
)
7055 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
7056 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
7058 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
7060 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
7061 sprintf(buf
, "%s %s", argv
[0], name
);
7062 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7065 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
7068 svccmd_list_append(&set_shows_list
, subcmd
);
7074 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
7075 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
7077 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
7079 subcmd
= set_shows_list
.list
[ii
];
7080 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
7082 reply("CSMSG_CHANNEL_OPTIONS_END");
7086 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7087 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7090 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7093 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
7095 reply("CSMSG_NO_ACCESS");
7101 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7105 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
7107 struct userData
*uData
;
7109 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7112 reply("CSMSG_NOT_USER", channel
->name
);
7118 /* Just show current option value. */
7120 else if(enabled_string(argv
[1]))
7122 uData
->flags
|= mask
;
7124 else if(disabled_string(argv
[1]))
7126 uData
->flags
&= ~mask
;
7130 reply("MSG_INVALID_BINARY", argv
[1]);
7134 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
7138 static MODCMD_FUNC(user_opt_autoop
)
7140 struct userData
*uData
;
7142 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7145 reply("CSMSG_NOT_USER", channel
->name
);
7148 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
7149 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
7151 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
7154 static MODCMD_FUNC(user_opt_autoinvite
)
7156 if((argc
> 1) && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
7158 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
7160 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
7163 static MODCMD_FUNC(user_opt_autojoin
)
7165 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
7168 static MODCMD_FUNC(user_opt_info
)
7170 struct userData
*uData
;
7173 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7177 /* If they got past the command restrictions (which require access)
7178 * but fail this test, we have some fool with security override on.
7180 reply("CSMSG_NOT_USER", channel
->name
);
7187 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
7188 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
7190 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
7193 bp
= strcspn(infoline
, "\001");
7196 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
7201 if(infoline
[0] == '*' && infoline
[1] == 0)
7204 uData
->info
= strdup(infoline
);
7207 reply("CSMSG_USET_INFO", uData
->info
);
7209 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
7213 struct svccmd_list uset_shows_list
;
7215 static CHANSERV_FUNC(cmd_uset
)
7217 struct svccmd
*subcmd
;
7221 /* Check if we need to (re-)initialize uset_shows_list. */
7222 if(!uset_shows_list
.used
)
7226 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7229 if(!uset_shows_list
.size
)
7231 uset_shows_list
.size
= ArrayLength(options
);
7232 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
7234 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
7236 const char *name
= options
[ii
];
7237 sprintf(buf
, "%s %s", argv
[0], name
);
7238 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7241 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
7244 svccmd_list_append(&uset_shows_list
, subcmd
);
7250 /* Do this so options are presented in a consistent order. */
7251 reply("CSMSG_USER_OPTIONS");
7252 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
7253 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
7257 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7258 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7261 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7265 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7268 static CHANSERV_FUNC(cmd_giveownership
)
7270 struct handle_info
*new_owner_hi
;
7271 struct userData
*new_owner
;
7272 struct userData
*curr_user
;
7273 struct userData
*invoker
;
7274 struct chanData
*cData
= channel
->channel_info
;
7275 struct do_not_register
*dnr
;
7276 struct giveownership
*giveownership
;
7277 const char *confirm
;
7278 unsigned int force
, override
;
7279 unsigned short co_access
, new_owner_old_access
;
7280 char transfer_reason
[MAXLEN
];
7283 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
7284 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
7286 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
7287 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
7288 && (uData
->access
> 500)
7289 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
7290 || uData
->access
< 500));
7293 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
7295 struct userData
*owner
= NULL
;
7296 for(curr_user
= channel
->channel_info
->users
;
7298 curr_user
= curr_user
->next
)
7300 if(curr_user
->access
!= UL_OWNER
)
7304 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
7311 else if(!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
7313 char delay
[INTERVALLEN
];
7314 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
7315 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
7319 reply("CSMSG_NO_OWNER", channel
->name
);
7322 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
7324 if(new_owner_hi
== user
->handle_info
)
7326 reply("CSMSG_NO_TRANSFER_SELF");
7329 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
7334 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_OWNER
- 1, 0, NULL
, 0);
7338 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
7342 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
7344 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
7347 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
7348 if(!IsHelping(user
))
7349 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
7351 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
7355 invoker
= GetChannelUser(cData
, user
->handle_info
);
7356 if(invoker
->access
<= UL_OWNER
)
7358 confirm
= make_confirmation_string(curr_user
);
7359 if((argc
< 3) || strcmp(argv
[2], confirm
))
7361 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi
->handle
, confirm
);
7366 new_owner_old_access
= new_owner
->access
;
7367 if(new_owner
->access
>= UL_COOWNER
)
7368 co_access
= new_owner
->access
;
7370 co_access
= UL_COOWNER
;
7371 new_owner
->access
= UL_OWNER
;
7373 curr_user
->access
= co_access
;
7374 cData
->ownerTransfer
= now
;
7376 giveownership
= calloc(1, sizeof(*giveownership
));
7377 giveownership
->issued
= now
;
7378 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
7379 giveownership
->target
= strdup(new_owner_hi
->handle
);
7380 giveownership
->target_access
= new_owner_old_access
;
7383 if(argc
> (2 + force
))
7385 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
7386 giveownership
->reason
= strdup(transfer_reason
);
7388 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
7391 giveownership
->previous
= channel
->channel_info
->giveownership
;
7392 channel
->channel_info
->giveownership
= giveownership
;
7394 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
7395 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
7396 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
7401 chanserv_expire_user_suspension(void *data
)
7403 struct userData
*target
= data
;
7405 target
->expires
= 0;
7406 target
->flags
&= ~USER_SUSPENDED
;
7409 static CHANSERV_FUNC(cmd_suspend
)
7411 struct handle_info
*hi
;
7412 struct userData
*actor
, *real_actor
, *target
;
7413 unsigned int override
= 0;
7417 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7418 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7419 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7420 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7422 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7425 if(target
->access
>= actor
->access
)
7427 reply("MSG_USER_OUTRANKED", hi
->handle
);
7430 if(target
->flags
& USER_SUSPENDED
)
7432 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
7437 target
->present
= 0;
7440 if(!strcmp(argv
[2], "0"))
7444 unsigned int duration
;
7445 if(!(duration
= ParseInterval(argv
[2])))
7447 reply("MSG_INVALID_DURATION", argv
[2]);
7450 expiry
= now
+ duration
;
7453 target
->expires
= expiry
;
7456 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
7458 if(!real_actor
|| target
->access
>= real_actor
->access
)
7459 override
= CMD_LOG_OVERRIDE
;
7460 target
->flags
|= USER_SUSPENDED
;
7461 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
7462 return 1 | override
;
7465 static CHANSERV_FUNC(cmd_unsuspend
)
7467 struct handle_info
*hi
;
7468 struct userData
*actor
= NULL
, *real_actor
= NULL
, *target
;
7469 unsigned int override
= 0;
7472 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7473 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7474 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7475 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7477 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7480 if(target
->access
>= actor
->access
)
7482 reply("MSG_USER_OUTRANKED", hi
->handle
);
7485 if(!(target
->flags
& USER_SUSPENDED
))
7487 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
7490 if(!real_actor
|| target
->access
>= real_actor
->access
)
7491 override
= CMD_LOG_OVERRIDE
;
7492 target
->flags
&= ~USER_SUSPENDED
;
7493 scan_user_presence(target
, NULL
);
7494 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
7495 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
7496 return 1 | override
;
7499 static MODCMD_FUNC(cmd_deleteme
)
7501 struct handle_info
*hi
;
7502 struct userData
*target
;
7503 const char *confirm_string
;
7504 unsigned short access
;
7507 hi
= user
->handle_info
;
7508 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7510 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7513 if(target
->access
== UL_OWNER
)
7515 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
7518 confirm_string
= make_confirmation_string(target
);
7519 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
7521 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
7524 access
= target
->access
;
7525 channel_name
= strdup(channel
->name
);
7526 del_channel_user(target
, 1);
7527 reply("CSMSG_DELETED_YOU", access
, channel_name
);
7533 chanserv_refresh_topics(UNUSED_ARG(void *data
))
7535 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7536 struct chanData
*cData
;
7539 for(cData
= channelList
; cData
; cData
= cData
->next
)
7541 if(IsSuspended(cData
))
7543 opt
= cData
->chOpts
[chTopicRefresh
];
7546 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
7549 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
7550 cData
->last_refresh
= refresh_num
;
7552 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
7556 chanserv_auto_resync(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
)) continue;
7565 opt
= cData
->chOpts
[chResync
];
7566 if(opt
== 'n') continue;
7567 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
7568 resync_channel(cData
->channel
);
7569 cData
->last_resync
= refresh_num
;
7571 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7574 static CHANSERV_FUNC(cmd_unf
)
7578 char response
[MAXLEN
];
7579 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7580 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7581 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7584 reply("CSMSG_UNF_RESPONSE");
7588 static CHANSERV_FUNC(cmd_ping
)
7592 char response
[MAXLEN
];
7593 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7594 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7595 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7598 reply("CSMSG_PING_RESPONSE");
7602 static CHANSERV_FUNC(cmd_wut
)
7606 char response
[MAXLEN
];
7607 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7608 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7609 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7612 reply("CSMSG_WUT_RESPONSE");
7616 static CHANSERV_FUNC(cmd_roulette
)
7619 struct chanData
*cData
= channel
->channel_info
;
7622 if (cData
->roulette_chamber
) {
7623 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7627 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7628 cData
->roulette_chamber
= 1 + rand() % 6;
7634 static CHANSERV_FUNC(cmd_shoot
)
7637 struct chanData
*cData
= channel
->channel_info
;
7639 if (cData
->roulette_chamber
<= 0) {
7640 struct service
*service
;
7641 if ((service
= service_find(chanserv
->nick
))) {
7642 reply("CSMSG_ROULETTE_NEW", service
->trigger
);
7647 cData
->roulette_chamber
--;
7649 if (cData
->roulette_chamber
== 0) {
7650 reply("CSMSG_ROULETTE_BANG");
7651 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7652 DelUser(user
, chanserv
, 1, "BANG!!!!");
7654 reply("CSMSG_ROULETTE_CLICK");
7661 chanserv_remove_abuse(void *data
)
7663 char *remnick
= data
;
7664 struct userNode
*user
;
7665 /* sometimes the clone was killed and maybe even the user took their nick back
7666 * (ie, an oper) so dont kill them here after all unless they are local. */
7667 if( (user
= GetUserH(remnick
)) )
7669 DelUser(user
, NULL
, 1, "");
7672 int lamepart(struct userNode
*nick
) {
7673 struct modeNode
*mn
;
7674 unsigned int count
, n
;
7676 for (n
=count
=0; n
<nick
->channels
.used
; n
++) {
7677 mn
= nick
->channels
.list
[n
];
7678 irc_svspart(chanserv
, nick
, mn
->channel
);
7684 static CHANSERV_FUNC(cmd_spin
)
7689 int type
= 0, lamep
= 1;
7692 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7700 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7701 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7702 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7704 if(chanserv_conf
.wheel
->used
< 1) {
7705 /* wheel actions not defined! eek */
7709 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7710 if(!wheel
&& *wheel
)
7713 /* enable this to be able to manually specify a result for testing:
7714 log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
7720 /* connection reset by peer */
7721 if (!strcasecmp(wheel
, "peer")) {
7722 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7724 irc_kill(chanserv
, user
, "Connection reset by peer");
7726 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7728 /* part all channels */
7729 else if (!strcasecmp(wheel
, "partall")) {
7730 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7734 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7736 /* random time gline */
7737 else if (!strcasecmp(wheel
, "gline")) {
7738 char target
[HOSTLEN
+ 3];
7739 int wtime
= 120 + rand() % 600;
7741 strcpy(target
, "*@");
7742 strcat(target
, user
->hostname
);
7743 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7745 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1, 0);
7746 // irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7749 else if (!strcasecmp(wheel
, "shun")) {
7750 char target
[HOSTLEN
+ 3];
7751 int wtime
= 120 + rand() % 600;
7753 strcpy(target
, "*@");
7754 strcat(target
, user
->hostname
);
7755 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SHUN");
7757 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1);
7759 /* absolutely nothing */
7760 else if (!strcasecmp(wheel
, "nothing")) {
7761 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7763 /* join random chans and part em several times */
7764 else if (!strcasecmp(wheel
, "randjoin")) {
7770 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7771 while(complete
!= 1) {
7772 if (rndchans
!= 15) {
7773 chango
= 120 + rand() % 600;
7774 sputsock("%s SJ %s #%d "FMT_TIME_T
, self
->numeric
, user
->numeric
, chango
, now
);
7777 if (roundz0r
!= 1) {
7781 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7788 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7794 /* abuse line added to /whois */
7795 else if (!strcasecmp(wheel
, "abusewhois")) {
7796 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7797 irc_swhois(chanserv
, user
, "is being defecated on by services");
7799 /* kick from each channel your in */
7800 else if (!strcasecmp(wheel
, "kickall")) {
7801 unsigned int count
, n
;
7802 struct modeNode
*mn
;
7804 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7806 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7807 mn
= user
->channels
.list
[n
];
7808 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7811 /* random nick change */
7812 else if (!strcasecmp(wheel
, "nickchange")) {
7813 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7815 char *oldnick
= NULL
;
7816 char *oldident
= NULL
;
7817 char *oldhost
= NULL
;
7818 char abusednick
[NICKLEN
] = "";
7819 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7820 struct userNode
*clone
;
7822 oldnick
= strdup(user
->nick
);
7823 oldident
= strdup(user
->ident
);
7824 oldhost
= strdup(user
->hostname
);
7826 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7828 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7829 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7830 if(!GetUserH(abusednick
))
7834 SVSNickChange(user
, abusednick
);
7835 irc_svsnick(chanserv
, user
, abusednick
);
7836 clone
= AddLocalUser(oldnick
, oldident
, oldhost
, "I got abused by the wheel of misfortune :D", "+i");
7837 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7840 else if (!strcasecmp(wheel
, "kill")) {
7841 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7843 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7844 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7846 /* service ignore */
7847 else if (!strcasecmp(wheel
, "svsignore")) {
7848 int gagged
, ignoretime
= 0;
7849 char target
[HOSTLEN
+ 13];
7852 /* we cant gag opers, so just verbally abuse them */
7853 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7856 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7858 strcpy(target
, "*!*@");
7859 strcat(target
, user
->hostname
);
7860 ignoretime
= now
+ (1 + rand() % 120);
7862 gagged
= gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7864 /* kick and ban from each channel your in */
7865 else if (!strcasecmp(wheel
, "kickbanall")) {
7866 unsigned int count
, n
;
7867 struct modeNode
*mn
;
7868 //char ban[HOSTLEN + 1];
7870 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7872 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7873 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7874 struct mod_chanmode
*change
;
7875 /* struct banData *bData; */
7876 unsigned int exists
;
7877 /* int duration = 300; */
7880 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7882 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7883 mn
= user
->channels
.list
[n
];
7884 if(mn
->channel
->banlist
.used
>= MAXBANS
) {
7885 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7890 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7892 change
= mod_chanmode_alloc(1);
7893 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7894 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7897 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7898 mod_chanmode_free(change
);
7900 exists
= ChannelBanExists(mn
->channel
, ban
);
7902 change
= mod_chanmode_alloc(1);
7903 change
->args
[0].mode
= MODE_BAN
;
7904 change
->args
[0].u
.hostmask
= ban
;
7906 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7907 mod_chanmode_free(change
);
7911 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7915 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7919 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7926 static CHANSERV_FUNC(cmd_8ball
)
7928 unsigned int i
, j
, accum
;
7933 for(i
=1; i
<argc
; i
++)
7934 for(j
=0; argv
[i
][j
]; j
++)
7935 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7936 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7939 char response
[MAXLEN
];
7940 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7941 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7944 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7948 #else /* Use cool 8ball instead */
7950 void eightball(char *outcome
, int method
, unsigned int seed
)
7954 #define NUMOFCOLORS 18
7955 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7956 "white", "black", "grey", "brown",
7957 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7958 "fuchsia","turquoise","magenta", "cyan"};
7959 #define NUMOFLOCATIONS 50
7960 char balllocations
[50][55] = {
7961 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7962 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7963 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7964 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7965 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7966 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7967 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7968 "your bra", "your hair", "your bed", "the couch", "the wall",
7969 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7970 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7971 #define NUMOFPREPS 15
7972 char ballpreps
[50][50] = {
7973 "Near", "Somewhere near", "In", "In", "In",
7974 "In", "Hiding in", "Under", "Next to", "Over",
7975 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7976 #define NUMOFNUMS 34
7977 char ballnums
[50][50] = {
7978 "A hundred", "A thousand", "A few", "42",
7979 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7980 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7981 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7983 #define NUMOFMULTS 8
7984 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7987 * 0: normal (Not used in x3)
7994 if (method
== 1) /* A Color */
7998 answer
= (rand() % 12); /* Make sure this is the # of entries */
8001 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
8003 case 1: strcpy(tmp
, "Sort of a light %s color.");
8005 case 2: strcpy(tmp
, "Dark and dreary %s.");
8007 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
8009 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
8011 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
8013 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
8015 case 10: strcpy(tmp
, "Solid %s.");
8017 case 11: strcpy(tmp
, "Transparent %s.");
8019 default: strcpy(outcome
, "An invalid random number was generated.");
8022 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
8025 else if (method
== 2) /* Location */
8027 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
8029 else if (method
== 3) /* Number of ___ */
8031 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
8035 //Debug(DBGWARNING, "Error in 8ball.");
8040 static CHANSERV_FUNC(cmd_8ball
)
8042 char *word1
, *word2
, *word3
;
8043 static char eb
[MAXLEN
];
8044 unsigned int accum
, i
, j
;
8048 for(i
=1; i
<argc
; i
++)
8049 for(j
=0; argv
[i
][j
]; j
++)
8050 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
8052 accum
+= time(NULL
)/3600;
8054 word2
= argc
>2?argv
[2]:"";
8055 word3
= argc
>3?argv
[3]:"";
8058 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
8059 eightball(eb
, 1, accum
);
8060 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
8061 eightball(eb
, 1, accum
);
8062 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
8063 eightball(eb
, 1, accum
);
8064 /*** LOCATION *****/
8069 (strcasecmp(word1
, "where") == 0) &&
8070 (strcasecmp(word2
, "is") == 0)
8074 strcasecmp(word1
, "where's") == 0
8077 eightball(eb
, 2, accum
);
8079 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
8080 eightball(eb
, 3, accum
);
8084 /* Generic 8ball question.. so pull from x3.conf srvx style */
8087 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
8090 char response
[MAXLEN
];
8091 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
8092 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8095 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
8101 char response
[MAXLEN
];
8102 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
8103 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8106 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
8111 static CHANSERV_FUNC(cmd_d
)
8113 unsigned long sides
, count
, modifier
, ii
, total
;
8114 char response
[MAXLEN
], *sep
;
8118 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
8128 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
8129 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
8133 else if((sep
[0] == '-') && isdigit(sep
[1]))
8134 modifier
= strtoul(sep
, NULL
, 10);
8135 else if((sep
[0] == '+') && isdigit(sep
[1]))
8136 modifier
= strtoul(sep
+1, NULL
, 10);
8143 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
8148 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
8151 for(total
= ii
= 0; ii
< count
; ++ii
)
8152 total
+= (rand() % sides
) + 1;
8155 if((count
> 1) || modifier
)
8157 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
8158 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
8162 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
8163 sprintf(response
, fmt
, total
, sides
);
8166 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8168 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8172 static CHANSERV_FUNC(cmd_huggle
)
8174 /* CTCP must be via PRIVMSG, never notice */
8176 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
8178 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
8182 static CHANSERV_FUNC(cmd_calc
)
8184 char response
[MAXLEN
];
8187 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8190 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8192 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8196 static CHANSERV_FUNC(cmd_reply
)
8200 unsplit_string(argv
+ 1, argc
- 1, NULL
);
8203 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8205 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
8210 chanserv_adjust_limit(void *data
)
8212 struct mod_chanmode change
;
8213 struct chanData
*cData
= data
;
8214 struct chanNode
*channel
= cData
->channel
;
8217 if(IsSuspended(cData
))
8220 cData
->limitAdjusted
= now
;
8221 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
8222 if(cData
->modes
.modes_set
& MODE_LIMIT
)
8224 if(limit
> cData
->modes
.new_limit
)
8225 limit
= cData
->modes
.new_limit
;
8226 else if(limit
== cData
->modes
.new_limit
)
8230 mod_chanmode_init(&change
);
8231 change
.modes_set
= MODE_LIMIT
;
8232 change
.new_limit
= limit
;
8233 mod_chanmode_announce(chanserv
, channel
, &change
);
8237 handle_new_channel(struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8239 struct chanData
*cData
;
8241 if(!(cData
= channel
->channel_info
))
8244 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
8245 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
8247 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
8248 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
8252 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
8254 struct banData
*bData
;
8255 struct mod_chanmode
*change
;
8257 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
8262 if (chan
->channel_info
) {
8263 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
8265 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0))
8277 check_bans(struct userNode
*user
, const char *channel
)
8279 struct chanNode
*chan
;
8280 struct mod_chanmode change
;
8281 struct chanData
*cData
;
8282 struct banData
*bData
;
8284 if (!(chan
= GetChannel(channel
)))
8287 if(!(cData
= chan
->channel_info
))
8290 mod_chanmode_init(&change
);
8293 if(chan
->banlist
.used
< MAXBANS
)
8295 /* Not joining through a ban. */
8296 for(bData
= cData
->bans
;
8297 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8298 bData
= bData
->next
);
8302 char kick_reason
[MAXLEN
];
8303 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8305 bData
->triggered
= now
;
8306 if(bData
!= cData
->bans
)
8308 /* Shuffle the ban to the head of the list. */
8310 bData
->next
->prev
= bData
->prev
;
8312 bData
->prev
->next
= bData
->next
;
8315 bData
->next
= cData
->bans
;
8318 cData
->bans
->prev
= bData
;
8320 cData
->bans
= bData
;
8323 change
.args
[0].mode
= MODE_BAN
;
8324 change
.args
[0].u
.hostmask
= bData
->mask
;
8325 mod_chanmode_announce(chanserv
, chan
, &change
);
8326 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8334 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
8337 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
8339 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
, 0))
8346 /* Welcome to my worst nightmare. Warning: Read (or modify)
8347 the code below at your own risk. */
8349 handle_join(struct modeNode
*mNode
, UNUSED_ARG(void *extra
))
8351 struct mod_chanmode change
;
8352 struct userNode
*user
= mNode
->user
;
8353 struct chanNode
*channel
= mNode
->channel
;
8354 struct chanData
*cData
;
8355 struct userData
*uData
= NULL
;
8356 struct banData
*bData
;
8357 struct handle_info
*handle
;
8358 unsigned int modes
= 0, info
= 0;
8361 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8364 cData
= channel
->channel_info
;
8365 if(channel
->members
.used
> cData
->max
)
8366 cData
->max
= channel
->members
.used
;
8369 /* Check for bans. If they're joining through a ban, one of two
8371 * 1: Join during a netburst, by riding the break. Kick them
8372 * unless they have ops or voice in the channel.
8373 * 2: They're allowed to join through the ban (an invite in
8374 * ircu2.10, or a +e on Hybrid, or something).
8375 * If they're not joining through a ban, and the banlist is not
8376 * full, see if they're on the banlist for the channel. If so,
8379 if(user
->uplink
->burst
&& !mNode
->modes
)
8382 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
8384 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
, 0))
8386 /* Riding a netburst. Naughty. */
8387 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
8394 if(user
->handle_info
)
8396 handle
= user
->handle_info
;
8399 uData
= GetTrueChannelAccess(cData
, handle
);
8404 mod_chanmode_init(&change
);
8407 /* TODO: maybe only people above inviteme level? -Rubin */
8408 /* We don't kick people with access */
8409 if(!uData
&& !channel_user_is_exempt(user
, channel
))
8411 if(channel
->banlist
.used
< MAXBANS
)
8413 /* Not joining through a ban. */
8414 for(bData
= cData
->bans
;
8415 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8416 bData
= bData
->next
);
8420 char kick_reason
[MAXLEN
];
8421 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8423 bData
->triggered
= now
;
8424 if(bData
!= cData
->bans
)
8426 /* Shuffle the ban to the head of the list. */
8428 bData
->next
->prev
= bData
->prev
;
8430 bData
->prev
->next
= bData
->next
;
8433 bData
->next
= cData
->bans
;
8436 cData
->bans
->prev
= bData
;
8437 cData
->bans
= bData
;
8440 change
.args
[0].mode
= MODE_BAN
;
8441 change
.args
[0].u
.hostmask
= bData
->mask
;
8442 mod_chanmode_announce(chanserv
, channel
, &change
);
8443 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8449 /* ChanServ will not modify the limits in join-flooded channels.
8450 It will also skip DynLimit processing when the user (or srvx)
8451 is bursting in, because there are likely more incoming. */
8452 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8453 && !user
->uplink
->burst
8454 && !channel
->join_flooded
8455 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
8457 /* The user count has begun "bumping" into the channel limit,
8458 so set a timer to raise the limit a bit. Any previous
8459 timers are removed so three incoming users within the delay
8460 results in one limit change, not three. */
8462 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8463 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8466 /* Give automodes exept during join-floods */
8467 if(!channel
->join_flooded
)
8469 if(cData
->chOpts
[chAutomode
] == 'v')
8470 modes
|= MODE_VOICE
;
8471 else if(cData
->chOpts
[chAutomode
] == 'h')
8472 modes
|= MODE_HALFOP
;
8473 else if(cData
->chOpts
[chAutomode
] == 'o')
8474 modes
|= MODE_CHANOP
;
8477 greeting
= cData
->greeting
;
8478 if(user
->handle_info
)
8480 /* handle = user->handle_info; */
8482 if(IsHelper(user
) && !IsHelping(user
))
8485 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8487 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
8489 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8495 /* uData = GetTrueChannelAccess(cData, handle); */
8496 if(uData
&& !IsUserSuspended(uData
))
8498 /* non users getting automodes are handled above. */
8499 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
8501 /* just op everyone with access */
8502 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8503 modes
|= MODE_VOICE
;
8504 /* or do their access level */
8505 else if(uData
->access
>= UL_OP
)
8506 modes
|= MODE_CHANOP
;
8507 else if(uData
->access
>= UL_HALFOP
)
8508 modes
|= MODE_HALFOP
;
8509 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
8510 modes
|= MODE_VOICE
;
8512 if(uData
->access
>= UL_PRESENT
)
8513 cData
->visited
= now
;
8514 if(cData
->user_greeting
)
8515 greeting
= cData
->user_greeting
;
8517 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
8518 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
8526 /* If user joining normally (not during burst), apply op or voice,
8527 * and send greeting/userinfo as appropriate.
8529 if(!user
->uplink
->burst
)
8533 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8534 if(modes & MODE_CHANOP) {
8535 modes &= ~MODE_HALFOP;
8536 modes &= ~MODE_VOICE;
8539 change
.args
[0].mode
= modes
;
8540 change
.args
[0].u
.member
= mNode
;
8541 mod_chanmode_announce(chanserv
, channel
, &change
);
8544 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
8546 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
8552 chanserv_autojoin_channels(struct userNode
*user
)
8554 struct userData
*channel
;
8556 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8558 struct chanNode
*cn
;
8559 struct modeNode
*mn
;
8561 if(IsUserSuspended(channel
)
8562 || IsSuspended(channel
->channel
)
8563 || !(cn
= channel
->channel
->channel
))
8566 mn
= GetUserMode(cn
, user
);
8569 if(!IsUserSuspended(channel
)
8570 && IsUserAutoJoin(channel
)
8571 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8573 && !user
->uplink
->burst
)
8574 irc_svsjoin(chanserv
, user
, cn
);
8580 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
), UNUSED_ARG(void *extra
))
8582 struct mod_chanmode change
;
8583 struct userData
*channel
;
8584 unsigned int ii
, jj
, i
;
8586 if(!user
->handle_info
)
8589 mod_chanmode_init(&change
);
8591 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8593 struct chanNode
*cn
;
8594 struct chanData
*cData
;
8595 struct modeNode
*mn
;
8596 if(IsUserSuspended(channel
)
8597 || IsSuspended(channel
->channel
)
8598 || !(cn
= channel
->channel
->channel
))
8601 cData
= cn
->channel_info
;
8602 mn
= GetUserMode(cn
, user
);
8605 if(!IsUserSuspended(channel
)
8606 && IsUserAutoInvite(channel
)
8607 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8609 && !user
->uplink
->burst
)
8610 irc_invite(chanserv
, user
, cn
);
8614 if(channel
->access
>= UL_PRESENT
)
8615 channel
->channel
->visited
= now
;
8617 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8619 if(channel
->access
>= UL_OP
)
8620 change
.args
[0].mode
= MODE_CHANOP
;
8621 else if(channel
->access
>= UL_HALFOP
)
8622 change
.args
[0].mode
= MODE_HALFOP
;
8623 else if(channel
->access
>= UL_PEON
)
8624 change
.args
[0].mode
= MODE_VOICE
;
8626 change
.args
[0].mode
= 0;
8627 change
.args
[0].u
.member
= mn
;
8628 if(change
.args
[0].mode
)
8629 mod_chanmode_announce(chanserv
, cn
, &change
);
8632 channel
->seen
= now
;
8633 channel
->present
= 1;
8636 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8638 struct chanNode
*chan
= user
->channels
.list
[ii
]->channel
;
8639 struct banData
*ban
;
8641 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8642 || !chan
->channel_info
8643 || IsSuspended(chan
->channel_info
))
8645 if(protect_user(user
, chanserv
, chan
->channel_info
, true))
8647 for(jj
= 0; jj
< chan
->banlist
.used
; ++jj
)
8648 if(user_matches_glob(user
, chan
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8650 if(jj
< chan
->banlist
.used
)
8652 for(ban
= chan
->channel_info
->bans
; ban
; ban
= ban
->next
)
8654 char kick_reason
[MAXLEN
];
8655 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
, 0))
8657 change
.args
[0].mode
= MODE_BAN
;
8658 change
.args
[0].u
.hostmask
= ban
->mask
;
8659 mod_chanmode_announce(chanserv
, chan
, &change
);
8660 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8661 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8662 ban
->triggered
= now
;
8667 if(IsSupportHelper(user
))
8669 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8671 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8673 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8679 if (user
->handle_info
->ignores
->used
) {
8680 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8681 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8685 if (user
->handle_info
->epithet
)
8686 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8688 /* process autojoin channels 5 seconds later as this sometimes
8689 happens before autohide */
8690 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8691 chanserv_autojoin_channels(user
);
8695 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
), UNUSED_ARG(void *extra
))
8697 struct chanData
*cData
;
8698 struct userData
*uData
;
8700 cData
= mn
->channel
->channel_info
;
8701 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8704 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8706 /* Allow for a bit of padding so that the limit doesn't
8707 track the user count exactly, which could get annoying. */
8708 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8710 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8711 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8715 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8717 scan_user_presence(uData
, mn
->user
);
8719 if (uData
->access
>= UL_PRESENT
)
8720 cData
->visited
= now
;
8723 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8726 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
) {
8727 struct chanNode
*channel
;
8728 struct userNode
*exclude
;
8729 /* When looking at the channel that is being /part'ed, we
8730 * have to skip over the client that is leaving. For
8731 * other channels, we must not do that.
8733 channel
= chanserv_conf
.support_channels
.list
[ii
];
8734 exclude
= (channel
== mn
->channel
) ? mn
->user
: NULL
;
8735 if(find_handle_in_channel(channel
, mn
->user
->handle_info
, exclude
))
8738 if(ii
== chanserv_conf
.support_channels
.used
)
8739 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8744 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8746 struct userData
*uData
;
8748 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8749 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8750 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8753 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8755 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8756 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8759 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8764 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
, UNUSED_ARG(void *extra
))
8766 struct chanData
*cData
;
8768 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8771 cData
= channel
->channel_info
;
8772 if(bad_topic(channel
, user
, channel
->topic
))
8773 { /* User doesnt have privs to set topics. Undo it */
8774 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8775 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8778 /* If there is a topic mask set, and the new topic doesnt match,
8779 * set the topic to mask + new_topic */
8780 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8782 char new_topic
[TOPICLEN
+1];
8783 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8786 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8787 /* and fall through to topicsnarf code below.. */
8789 else /* Topic couldnt fit into mask, was too long */
8791 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8792 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8793 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8797 /* With topicsnarf, grab the topic and save it as the default topic. */
8798 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8801 cData
->topic
= strdup(channel
->topic
);
8807 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
, UNUSED_ARG(void *extra
))
8809 struct mod_chanmode
*bounce
= NULL
;
8810 unsigned int bnc
, ii
;
8813 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8816 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8817 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8819 char correct
[MAXLEN
];
8820 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8821 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8822 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8824 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8826 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8828 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8829 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8832 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8835 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8836 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8837 if(bounce
->args
[bnc
].u
.member
)
8841 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8842 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8844 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8846 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8848 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8849 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8852 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8853 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8854 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8857 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8859 const char *ban
= change
->args
[ii
].u
.hostmask
;
8860 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8863 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8864 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8865 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8867 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8872 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8873 mod_chanmode_announce(chanserv
, channel
, bounce
);
8874 for(ii
= 0; ii
< change
->argc
; ++ii
)
8875 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8876 free((char*)bounce
->args
[ii
].u
.hostmask
);
8877 mod_chanmode_free(bounce
);
8882 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
), UNUSED_ARG(void *extra
))
8884 struct chanNode
*channel
;
8885 struct banData
*bData
;
8886 struct mod_chanmode change
;
8887 unsigned int ii
, jj
;
8888 char kick_reason
[MAXLEN
];
8890 mod_chanmode_init(&change
);
8892 change
.args
[0].mode
= MODE_BAN
;
8893 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8895 channel
= user
->channels
.list
[ii
]->channel
;
8896 /* Need not check for bans if they're opped or voiced. */
8897 /* TODO: does this make sense in automode v, h, and o? *
8898 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8899 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8901 /* Need not check for bans unless channel registration is active. */
8902 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8904 /* Look for a matching ban already on the channel. */
8905 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8906 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8908 /* Need not act if we found one. */
8909 if(jj
< channel
->banlist
.used
)
8911 /* don't kick someone on the userlist */
8912 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8914 /* Look for a matching ban in this channel. */
8915 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8917 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
8919 change
.args
[0].u
.hostmask
= bData
->mask
;
8920 mod_chanmode_announce(chanserv
, channel
, &change
);
8921 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8922 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8923 bData
->triggered
= now
;
8924 break; /* we don't need to check any more bans in the channel */
8929 static void handle_rename(struct handle_info
*handle
, const char *old_handle
, UNUSED_ARG(void *extra
))
8931 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8935 dict_remove2(handle_dnrs
, old_handle
, 1);
8936 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8937 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8942 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
, UNUSED_ARG(void *extra
))
8944 struct userNode
*h_user
;
8946 if(handle
->channels
)
8948 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8949 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8951 while(handle
->channels
)
8952 del_channel_user(handle
->channels
, 1);
8957 handle_server_link(UNUSED_ARG(struct server
*server
), UNUSED_ARG(void *extra
))
8959 struct chanData
*cData
;
8961 for(cData
= channelList
; cData
; cData
= cData
->next
)
8963 if(!IsSuspended(cData
))
8964 cData
->may_opchan
= 1;
8965 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8966 && !cData
->channel
->join_flooded
8967 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8968 < chanserv_conf
.adjust_threshold
))
8970 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8971 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8978 chanserv_conf_read(void)
8982 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
8983 struct mod_chanmode
*change
;
8984 struct string_list
*strlist
;
8985 struct chanNode
*chan
;
8988 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
8990 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
8993 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8994 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8995 chanserv_conf
.support_channels
.used
= 0;
8996 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
8998 for(ii
= 0; ii
< strlist
->used
; ++ii
)
9000 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9003 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
9005 channelList_append(&chanserv_conf
.support_channels
, chan
);
9008 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
9011 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9014 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
9016 channelList_append(&chanserv_conf
.support_channels
, chan
);
9018 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
9019 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
9020 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
9021 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
9022 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
9023 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
9024 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
9025 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
9026 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
9027 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
9028 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
9029 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
9030 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
9031 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
9032 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
9033 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
9034 str
= database_get_data(conf_node
, KEY_DNR_EXPIRE_FREQ
, RECDB_QSTRING
);
9035 chanserv_conf
.dnr_expire_frequency
= str
? ParseInterval(str
) : 3600;
9036 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
9037 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
9038 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
9039 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
9040 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
9041 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
9042 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
9043 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
9044 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
9046 NickChange(chanserv
, str
, 0);
9047 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
9048 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
9049 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
9050 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
9051 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
9052 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
9053 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
9054 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
9055 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
9056 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
9057 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
9058 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
9059 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
9060 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
9061 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
9062 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
9063 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
9064 god_timeout
= str
? ParseInterval(str
) : 60*15;
9065 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
9068 safestrncpy(mode_line
, str
, sizeof(mode_line
));
9069 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
9070 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
9071 && (change
->argc
< 2))
9073 chanserv_conf
.default_modes
= *change
;
9074 mod_chanmode_free(change
);
9076 str
= database_get_data(conf_node
, KEY_VALID_CHANNEL_REGEX
, RECDB_QSTRING
);
9077 if (chanserv_conf
.valid_channel_regex_set
)
9078 regfree(&chanserv_conf
.valid_channel_regex
);
9080 int err
= regcomp(&chanserv_conf
.valid_channel_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
9081 chanserv_conf
.valid_channel_regex_set
= !err
;
9082 if (err
) log_module(CS_LOG
, LOG_ERROR
, "Bad valid_channel_regex (error %d)", err
);
9084 chanserv_conf
.valid_channel_regex_set
= 0;
9086 free_string_list(chanserv_conf
.wheel
);
9087 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
9089 strlist
= string_list_copy(strlist
);
9092 static const char *list
[] = {
9093 "peer", "partall", "gline", /* "shun", */
9094 "nothing", "randjoin", "abusewhois", "kickall",
9095 "nickchange", "kill", "svsignore", "kickbanall",
9098 strlist
= alloc_string_list(ArrayLength(list
)-1);
9099 for(ii
=0; list
[ii
]; ii
++)
9100 string_list_append(strlist
, strdup(list
[ii
]));
9102 chanserv_conf
.wheel
= strlist
;
9104 free_string_list(chanserv_conf
.set_shows
);
9105 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
9107 strlist
= string_list_copy(strlist
);
9110 static const char *list
[] = {
9111 /* free form text */
9112 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9113 /* options based on user level */
9114 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9115 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
9116 /* multiple choice options */
9117 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
9118 /* binary options */
9119 "DynLimit", "NoDelete", "BanTimeout",
9123 strlist
= alloc_string_list(ArrayLength(list
)-1);
9124 for(ii
=0; list
[ii
]; ii
++)
9125 string_list_append(strlist
, strdup(list
[ii
]));
9127 chanserv_conf
.set_shows
= strlist
;
9128 /* We don't look things up now, in case the list refers to options
9129 * defined by modules initialized after this point. Just mark the
9130 * function list as invalid, so it will be initialized.
9132 set_shows_list
.used
= 0;
9134 free_string_list(chanserv_conf
.eightball
);
9135 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
9138 strlist
= string_list_copy(strlist
);
9142 strlist
= alloc_string_list(4);
9143 string_list_append(strlist
, strdup("Yes."));
9144 string_list_append(strlist
, strdup("No."));
9145 string_list_append(strlist
, strdup("Maybe so."));
9147 chanserv_conf
.eightball
= strlist
;
9149 free_string_list(chanserv_conf
.old_ban_names
);
9150 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
9152 strlist
= string_list_copy(strlist
);
9154 strlist
= alloc_string_list(2);
9155 chanserv_conf
.old_ban_names
= strlist
;
9156 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
9157 off_channel
= str
? atoi(str
) : 0;
9161 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
9164 struct note_type
*ntype
;
9167 if(!(obj
= GET_RECORD_OBJECT(rd
)))
9169 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
9172 if(!(ntype
= chanserv_create_note_type(key
)))
9174 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
9178 /* Figure out set access */
9179 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
9181 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9182 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
9184 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
9186 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
9187 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
9189 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
9191 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
9195 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
9196 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9197 ntype
->set_access
.min_opserv
= 0;
9200 /* Figure out visibility */
9201 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
9202 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9203 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
9204 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9205 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
9206 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
9207 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
9208 ntype
->visible_type
= NOTE_VIS_ALL
;
9210 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9212 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
9213 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
9217 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9219 struct handle_info
*handle
;
9220 struct userData
*uData
;
9221 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
9223 unsigned short access_level
, lastaccess
= 0;
9225 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9227 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
9231 access_level
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
9232 if(access_level
> UL_OWNER
)
9234 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
9238 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
9239 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
9240 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
9241 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
9242 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9243 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
9244 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
9245 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
9246 lastaccess
= lstacc
? atoi(lstacc
) : 0;
9248 handle
= get_handle_info(key
);
9251 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
9255 uData
= add_channel_user(chan
, handle
, access_level
, last_seen
, inf
, 0);
9256 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
9257 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
9259 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
9260 if (uData
->accessexpiry
> 0)
9261 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
9263 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
9264 if (uData
->clvlexpiry
> 0)
9265 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
9267 uData
->lastaccess
= lastaccess
;
9269 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
9271 if(uData
->expires
> now
)
9272 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
9274 uData
->flags
&= ~USER_SUSPENDED
;
9277 /* Upgrade: set autoop to the inverse of noautoop */
9278 if(chanserv_read_version
< 2)
9280 /* if noautoop is true, set autoop false, and vice versa */
9281 if(uData
->flags
& USER_NOAUTO_OP
)
9282 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
9284 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
9285 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
);
9291 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9293 struct banData
*bData
;
9294 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
9295 time_t set_time
, triggered_time
, expires_time
;
9297 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9299 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
9303 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
9304 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
9305 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
9306 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9307 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
9308 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
9309 if (!reason
|| !owner
)
9312 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
9313 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
9315 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
9317 expires_time
= set_time
+ atoi(s_duration
);
9321 if(!reason
|| (expires_time
&& (expires_time
< now
)))
9324 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
9327 static struct suspended
*
9328 chanserv_read_suspended(dict_t obj
)
9330 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
9334 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
9335 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9336 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
9337 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9338 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9339 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9340 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
9341 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
9342 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9343 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
9347 static struct giveownership
*
9348 chanserv_read_giveownership(dict_t obj
)
9350 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
9354 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
9355 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
9357 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
9359 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
9360 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
9362 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
9363 giveownership
->reason
= str
? strdup(str
) : NULL
;
9364 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9365 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9367 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9368 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
9369 return giveownership
;
9373 chanserv_channel_read(const char *key
, struct record_data
*hir
)
9375 struct suspended
*suspended
;
9376 struct giveownership
*giveownership
;
9377 struct mod_chanmode
*modes
;
9378 struct chanNode
*cNode
;
9379 struct chanData
*cData
;
9380 struct dict
*channel
, *obj
;
9381 char *str
, *argv
[10];
9385 channel
= hir
->d
.object
;
9387 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
9390 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
9393 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
9396 cData
= register_channel(cNode
, str
);
9399 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
9403 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
9405 enum levelOption lvlOpt
;
9406 enum charOption chOpt
;
9408 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
9409 cData
->flags
= atoi(str
);
9411 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9413 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
9415 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
9416 else if(levelOptions
[lvlOpt
].old_flag
)
9418 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9419 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
9421 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
9425 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9427 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
9429 cData
->chOpts
[chOpt
] = str
[0];
9432 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
9434 enum levelOption lvlOpt
;
9435 enum charOption chOpt
;
9438 cData
->flags
= base64toint(str
, 5);
9439 count
= strlen(str
+= 5);
9440 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9443 if(levelOptions
[lvlOpt
].old_flag
)
9445 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9446 lvl
= levelOptions
[lvlOpt
].flag_value
;
9448 lvl
= levelOptions
[lvlOpt
].default_value
;
9450 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
9452 case 'c': lvl
= UL_COOWNER
; break;
9453 case 'm': lvl
= UL_MANAGER
; break;
9454 case 'n': lvl
= UL_OWNER
+1; break;
9455 case 'o': lvl
= UL_OP
; break;
9456 case 'p': lvl
= UL_PEON
; break;
9457 case 'h': lvl
= UL_HALFOP
; break;
9458 case 'w': lvl
= UL_OWNER
; break;
9459 default: lvl
= 0; break;
9461 cData
->lvlOpts
[lvlOpt
] = lvl
;
9463 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9464 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
9467 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
9469 suspended
= chanserv_read_suspended(obj
);
9470 cData
->suspended
= suspended
;
9471 suspended
->cData
= cData
;
9472 /* We could use suspended->expires and suspended->revoked to
9473 * set the CHANNEL_SUSPENDED flag, but we don't. */
9475 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
9477 suspended
= calloc(1, sizeof(*suspended
));
9478 suspended
->issued
= 0;
9479 suspended
->revoked
= 0;
9480 suspended
->suspender
= strdup(str
);
9481 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
9482 suspended
->expires
= str
? atoi(str
) : 0;
9483 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
9484 suspended
->reason
= strdup(str
? str
: "No reason");
9485 suspended
->previous
= NULL
;
9486 cData
->suspended
= suspended
;
9487 suspended
->cData
= cData
;
9491 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9492 suspended
= NULL
; /* to squelch a warning */
9495 if(IsSuspended(cData
)) {
9496 if(suspended
->expires
> now
)
9497 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
9498 else if(suspended
->expires
)
9499 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9502 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
9504 giveownership
= chanserv_read_giveownership(obj
);
9505 cData
->giveownership
= giveownership
;
9508 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
9509 struct mod_chanmode change
;
9510 mod_chanmode_init(&change
);
9512 change
.args
[0].mode
= MODE_CHANOP
;
9513 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
9514 mod_chanmode_announce(chanserv
, cNode
, &change
);
9517 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
9518 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9519 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
9520 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9521 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
9522 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9523 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
9524 cData
->max
= str
? atoi(str
) : 0;
9525 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
9526 cData
->greeting
= str
? strdup(str
) : NULL
;
9527 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
9528 cData
->user_greeting
= str
? strdup(str
) : NULL
;
9529 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
9530 cData
->topic_mask
= str
? strdup(str
) : NULL
;
9531 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
9532 cData
->topic
= str
? strdup(str
) : NULL
;
9534 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
9535 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
9537 if(!IsSuspended(cData
)
9538 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
9539 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
9540 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
9541 cData
->modes
= *modes
;
9543 cData
->modes
.modes_set
|= MODE_REGISTERED
;
9544 if(cData
->modes
.argc
> 1)
9545 cData
->modes
.argc
= 1;
9546 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
9547 mod_chanmode_free(modes
);
9550 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
9551 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9552 user_read_helper(iter_key(it
), iter_data(it
), cData
);
9554 if(!cData
->users
&& !IsProtected(cData
))
9556 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
9557 unregister_channel(cData
, "has empty user list.");
9561 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
9562 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9563 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
9565 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
9566 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9568 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
9569 struct record_data
*rd
= iter_data(it
);
9570 const char *note
, *setter
;
9572 if(rd
->type
!= RECDB_OBJECT
)
9574 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9578 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9580 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9582 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9586 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9587 if(!setter
) setter
= "<unknown>";
9588 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9596 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9598 const char *setter
, *reason
, *str
;
9599 struct do_not_register
*dnr
;
9602 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9605 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9608 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9611 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9614 str
= database_get_data(hir
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9615 expiry
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9616 if(expiry
&& expiry
<= now
)
9618 dnr
= chanserv_add_dnr(key
, setter
, expiry
, reason
);
9621 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9623 dnr
->set
= atoi(str
);
9629 chanserv_version_read(struct dict
*section
)
9633 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9635 chanserv_read_version
= atoi(str
);
9636 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9640 chanserv_saxdb_read(struct dict
*database
)
9642 struct dict
*section
;
9645 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9646 chanserv_version_read(section
);
9648 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9649 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9650 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9652 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9653 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9654 chanserv_channel_read(iter_key(it
), iter_data(it
));
9656 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9657 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9658 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9664 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9666 int high_present
= 0;
9667 saxdb_start_record(ctx
, KEY_USERS
, 1);
9668 for(; uData
; uData
= uData
->next
)
9670 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9672 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9673 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9674 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9675 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9676 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9677 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9679 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9681 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9683 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9684 saxdb_end_record(ctx
);
9686 saxdb_end_record(ctx
);
9687 return high_present
;
9691 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9695 saxdb_start_record(ctx
, KEY_BANS
, 1);
9696 for(; bData
; bData
= bData
->next
)
9698 saxdb_start_record(ctx
, bData
->mask
, 0);
9699 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9700 if(bData
->triggered
)
9701 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9703 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9705 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9707 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9708 saxdb_end_record(ctx
);
9710 saxdb_end_record(ctx
);
9714 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9716 saxdb_start_record(ctx
, name
, 0);
9717 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9718 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9720 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9722 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9724 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9726 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9727 saxdb_end_record(ctx
);
9731 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9733 saxdb_start_record(ctx
, name
, 0);
9734 if(giveownership
->staff_issuer
)
9735 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9736 if(giveownership
->old_owner
)
9737 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9738 if(giveownership
->target
)
9739 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9740 if(giveownership
->target_access
)
9741 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9742 if(giveownership
->reason
)
9743 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9744 if(giveownership
->issued
)
9745 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9746 if(giveownership
->previous
)
9747 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9748 saxdb_end_record(ctx
);
9752 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9756 enum levelOption lvlOpt
;
9757 enum charOption chOpt
;
9759 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9761 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9762 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9764 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9765 if(channel
->registrar
)
9766 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9767 if(channel
->greeting
)
9768 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9769 if(channel
->user_greeting
)
9770 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9771 if(channel
->topic_mask
)
9772 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9773 if(channel
->suspended
)
9774 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9775 if(channel
->giveownership
)
9776 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9778 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9779 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9780 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9781 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9782 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9784 buf
[0] = channel
->chOpts
[chOpt
];
9786 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9788 saxdb_end_record(ctx
);
9790 if (channel
->maxsetinfo
)
9791 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9793 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9795 mod_chanmode_format(&channel
->modes
, buf
);
9796 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9799 high_present
= chanserv_write_users(ctx
, channel
->users
);
9800 chanserv_write_bans(ctx
, channel
->bans
);
9802 if(dict_size(channel
->notes
))
9806 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9807 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9809 struct note
*note
= iter_data(it
);
9810 saxdb_start_record(ctx
, iter_key(it
), 0);
9811 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9812 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9813 saxdb_end_record(ctx
);
9815 saxdb_end_record(ctx
);
9818 if(channel
->ownerTransfer
)
9819 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9820 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9821 saxdb_end_record(ctx
);
9825 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9829 saxdb_start_record(ctx
, ntype
->name
, 0);
9830 switch(ntype
->set_access_type
)
9832 case NOTE_SET_CHANNEL_ACCESS
:
9833 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9835 case NOTE_SET_CHANNEL_SETTER
:
9836 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9838 case NOTE_SET_PRIVILEGED
: default:
9839 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9842 switch(ntype
->visible_type
)
9844 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9845 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9846 case NOTE_VIS_PRIVILEGED
:
9847 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9849 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9850 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9851 saxdb_end_record(ctx
);
9855 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9857 struct do_not_register
*dnr
;
9858 dict_iterator_t it
, next
;
9860 for(it
= dict_first(dnrs
); it
; it
= next
)
9862 next
= iter_next(it
);
9863 dnr
= iter_data(it
);
9864 if(dnr
->expires
&& dnr
->expires
<= now
)
9866 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9868 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9871 dict_remove(dnrs
, iter_key(it
));
9872 saxdb_write_int(ctx
, KEY_EXPIRES
, dnr
->expires
);
9874 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9875 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9876 saxdb_end_record(ctx
);
9881 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9884 struct chanData
*channel
;
9886 /* Version Control*/
9887 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9888 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9889 saxdb_end_record(ctx
);
9892 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9893 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9894 chanserv_write_note_type(ctx
, iter_data(it
));
9895 saxdb_end_record(ctx
);
9898 saxdb_start_record(ctx
, KEY_DNR
, 1);
9899 write_dnrs_helper(ctx
, handle_dnrs
);
9900 write_dnrs_helper(ctx
, plain_dnrs
);
9901 write_dnrs_helper(ctx
, mask_dnrs
);
9902 saxdb_end_record(ctx
);
9905 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9906 for(channel
= channelList
; channel
; channel
= channel
->next
)
9907 chanserv_write_channel(ctx
, channel
);
9908 saxdb_end_record(ctx
);
9914 chanserv_db_cleanup(UNUSED_ARG(void *extra
)) {
9916 unreg_part_func(handle_part
, NULL
);
9918 unregister_channel(channelList
, "terminating.");
9919 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9920 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9921 free(chanserv_conf
.support_channels
.list
);
9922 dict_delete(handle_dnrs
);
9923 dict_delete(plain_dnrs
);
9924 dict_delete(mask_dnrs
);
9925 dict_delete(note_types
);
9926 free_string_list(chanserv_conf
.eightball
);
9927 free_string_list(chanserv_conf
.old_ban_names
);
9928 free_string_list(chanserv_conf
.wheel
);
9929 free_string_list(chanserv_conf
.set_shows
);
9930 free(set_shows_list
.list
);
9931 free(uset_shows_list
.list
);
9934 struct userData
*helper
= helperList
;
9935 helperList
= helperList
->next
;
9940 #if defined(GCC_VARMACROS)
9941 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
9942 #elif defined(C99_VARMACROS)
9943 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
9945 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9946 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9949 init_chanserv(const char *nick
)
9951 struct chanNode
*chan
;
9954 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9955 conf_register_reload(chanserv_conf_read
);
9958 reg_server_link_func(handle_server_link
, NULL
);
9959 reg_new_channel_func(handle_new_channel
, NULL
);
9960 reg_join_func(handle_join
, NULL
);
9961 reg_part_func(handle_part
, NULL
);
9962 reg_kick_func(handle_kick
, NULL
);
9963 reg_topic_func(handle_topic
, NULL
);
9964 reg_mode_change_func(handle_mode
, NULL
);
9965 reg_nick_change_func(handle_nick_change
, NULL
);
9966 reg_auth_func(handle_auth
, NULL
);
9969 reg_handle_rename_func(handle_rename
, NULL
);
9970 reg_unreg_func(handle_unreg
, NULL
);
9972 handle_dnrs
= dict_new();
9973 dict_set_free_data(handle_dnrs
, free
);
9974 plain_dnrs
= dict_new();
9975 dict_set_free_data(plain_dnrs
, free
);
9976 mask_dnrs
= dict_new();
9977 dict_set_free_data(mask_dnrs
, free
);
9979 reg_svccmd_unbind_func(handle_svccmd_unbind
, NULL
);
9980 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
9981 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+channel", NULL
);
9982 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9983 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
9984 DEFINE_COMMAND(dnrsearch
, 3, 0, "template", "noregister", NULL
);
9985 modcmd_register(chanserv_module
, "dnrsearch print", NULL
, 0, 0, NULL
);
9986 modcmd_register(chanserv_module
, "dnrsearch remove", NULL
, 0, 0, NULL
);
9987 modcmd_register(chanserv_module
, "dnrsearch count", NULL
, 0, 0, NULL
);
9988 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
9989 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9990 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9991 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
9992 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
9994 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9996 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
9997 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
9999 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10000 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10001 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10002 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10003 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10005 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
10006 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
10007 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
10008 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10009 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10010 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10011 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10013 DEFINE_COMMAND(levels
, 1, 0, NULL
);
10015 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10016 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
10017 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10018 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
10020 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10021 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
10022 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10023 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10024 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10025 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10026 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10027 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10028 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10029 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10031 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10032 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10033 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10034 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
10035 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
10036 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
10037 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
10038 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
10039 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
10040 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
10041 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
10042 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
10043 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10044 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10046 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10047 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10048 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10049 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10051 /* if you change dellamer access, see also places
10052 * like unbanme which have manager hardcoded. */
10053 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10054 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
10056 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
10058 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
10060 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10061 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10062 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10063 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10064 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10065 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10066 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10067 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10068 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10069 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10070 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10071 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10073 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
10074 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
10076 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
10077 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
10078 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
10079 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
10081 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10082 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10083 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
10084 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
10085 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
10087 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10088 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10089 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10090 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10091 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10092 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10093 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10094 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10095 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10096 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10097 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
10099 /* Channel options */
10100 DEFINE_CHANNEL_OPTION(defaulttopic
);
10101 DEFINE_CHANNEL_OPTION(topicmask
);
10102 DEFINE_CHANNEL_OPTION(greeting
);
10103 DEFINE_CHANNEL_OPTION(usergreeting
);
10104 DEFINE_CHANNEL_OPTION(modes
);
10105 DEFINE_CHANNEL_OPTION(enfops
);
10106 DEFINE_CHANNEL_OPTION(enfhalfops
);
10107 DEFINE_CHANNEL_OPTION(automode
);
10108 DEFINE_CHANNEL_OPTION(protect
);
10109 DEFINE_CHANNEL_OPTION(enfmodes
);
10110 DEFINE_CHANNEL_OPTION(enftopic
);
10111 DEFINE_CHANNEL_OPTION(pubcmd
);
10112 DEFINE_CHANNEL_OPTION(userinfo
);
10113 DEFINE_CHANNEL_OPTION(dynlimit
);
10114 DEFINE_CHANNEL_OPTION(topicsnarf
);
10115 DEFINE_CHANNEL_OPTION(nodelete
);
10116 DEFINE_CHANNEL_OPTION(toys
);
10117 DEFINE_CHANNEL_OPTION(setters
);
10118 DEFINE_CHANNEL_OPTION(topicrefresh
);
10119 DEFINE_CHANNEL_OPTION(resync
);
10120 DEFINE_CHANNEL_OPTION(ctcpreaction
);
10121 DEFINE_CHANNEL_OPTION(bantimeout
);
10122 DEFINE_CHANNEL_OPTION(inviteme
);
10123 DEFINE_CHANNEL_OPTION(unreviewed
);
10124 modcmd_register(chanserv_module
, "set unreviewed on", NULL
, 0, 0, "flags", "+helping", NULL
);
10125 modcmd_register(chanserv_module
, "set unreviewed off", NULL
, 0, 0, "flags", "+oper", NULL
);
10126 DEFINE_CHANNEL_OPTION(maxsetinfo
);
10127 if(off_channel
> 1)
10128 DEFINE_CHANNEL_OPTION(offchannel
);
10129 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
10131 /* Alias set topic to set defaulttopic for compatibility. */
10132 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
10135 DEFINE_USER_OPTION(autoinvite
);
10136 DEFINE_USER_OPTION(autojoin
);
10137 DEFINE_USER_OPTION(info
);
10138 DEFINE_USER_OPTION(autoop
);
10140 /* Alias uset autovoice to uset autoop. */
10141 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
10143 note_types
= dict_new();
10144 dict_set_free_data(note_types
, chanserv_deref_note_type
);
10147 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
10148 chanserv
= AddLocalUser(nick
, nick
, NULL
, "Channel Services", modes
);
10149 service_register(chanserv
)->trigger
= '!';
10150 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
, NULL
);
10153 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
10155 if(chanserv_conf
.channel_expire_frequency
)
10156 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
10158 if(chanserv_conf
.dnr_expire_frequency
)
10159 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
10161 if(chanserv_conf
.ban_timeout_frequency
)
10162 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
10164 if(chanserv_conf
.refresh_period
)
10166 time_t next_refresh
;
10167 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
10168 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
10169 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
10172 if (autojoin_channels
&& chanserv
) {
10173 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
10174 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
10175 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
10179 reg_exit_func(chanserv_db_cleanup
, NULL
);
10180 message_register_table(msgtab
);