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." },
265 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
267 /* Channel management */
268 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
269 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
270 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
272 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
273 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
274 { "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" },
275 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
276 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
277 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
278 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
280 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
281 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
282 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
283 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
284 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
285 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
286 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
287 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
288 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
289 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
290 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
291 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
292 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
293 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
294 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
295 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
296 { "CSMSG_SET_MODES", "$bModes $b %s" },
297 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
298 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
299 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
300 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
301 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
302 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
303 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
304 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
305 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
306 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
307 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
308 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
309 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
310 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
311 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
312 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
313 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
314 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %d - %s" },
315 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
316 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
317 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
319 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
320 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
321 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
322 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
323 { "CSMSG_USET_INFO", "$bInfo $b %s" },
325 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
326 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
327 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
328 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
329 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
330 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
331 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
332 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
333 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
334 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
335 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
337 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
338 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
339 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
340 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
341 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
342 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
343 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
345 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
346 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
347 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
348 { "CSMSG_PROTECT_NONE", "No users will be protected." },
349 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
350 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
351 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
353 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
354 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
355 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
356 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
357 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
359 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
360 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
361 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
362 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
363 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
365 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
366 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
367 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
368 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
369 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
371 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
372 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
373 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
374 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
375 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
376 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
378 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
379 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
380 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
381 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
382 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
383 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
384 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
385 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
386 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
388 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
389 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
390 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
392 /* Channel userlist */
393 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
394 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
395 /* uncomment if needed to adujust styles (and change code below)
396 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
397 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
398 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
399 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
400 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
401 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
403 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
404 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
405 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
407 /* Channel note list */
408 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
409 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
410 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
411 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
412 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
413 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
414 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
415 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
416 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
417 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
418 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
419 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
420 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
421 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
422 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
424 /* Channel [un]suspension */
425 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
426 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
427 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
428 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
429 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
430 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
431 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
433 /* Access information */
434 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
435 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
436 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
437 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
438 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
439 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
440 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
441 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
442 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
443 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
444 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
445 { "CSMSG_UC_H_TITLE", "network helper" },
446 { "CSMSG_LC_H_TITLE", "support helper" },
447 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
449 /* Seen information */
450 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
451 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
452 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
453 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
455 /* Names information */
456 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
457 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
459 /* Channel information */
460 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
461 { "CSMSG_BAR", "----------------------------------------"},
462 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
463 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
464 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
465 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
466 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
467 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
468 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
469 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
470 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
471 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
472 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
473 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
474 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
475 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
476 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
477 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
478 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
479 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
480 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
481 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
482 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
483 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
484 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
485 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
486 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
487 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
489 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
490 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
491 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
492 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
493 { "CSMSG_PEEK_OPS", "$bOps:$b" },
494 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
495 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
497 /* Network information */
498 { "CSMSG_NETWORK_INFO", "Network Information:" },
499 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
500 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
501 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
502 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
503 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
504 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
505 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
506 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
509 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
510 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
511 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
513 /* Channel searches */
514 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
515 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
516 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
517 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
519 /* Channel configuration */
520 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
521 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
522 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
523 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
524 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
527 { "CSMSG_USER_OPTIONS", "User Options:" },
528 // { "CSMSG_USER_PROTECTED", "That user is protected." },
531 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
532 { "CSMSG_PING_RESPONSE", "Pong!" },
533 { "CSMSG_WUT_RESPONSE", "wut" },
534 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
535 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
536 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
537 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
538 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
539 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
540 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
541 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
542 { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
543 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
544 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
545 { "CSMSG_ROULETTE_CLICK", "Click" } ,
547 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
548 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
549 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
551 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
552 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
553 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
554 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
555 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
556 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
557 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
558 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
559 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
560 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
561 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
562 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
563 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
564 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
567 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
568 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
569 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
570 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
574 /* eject_user and unban_user flags */
575 #define ACTION_KICK 0x0001
576 #define ACTION_BAN 0x0002
577 #define ACTION_ADD_LAMER 0x0004
578 #define ACTION_ADD_TIMED_LAMER 0x0008
579 #define ACTION_UNBAN 0x0010
580 #define ACTION_DEL_LAMER 0x0020
582 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
583 #define MODELEN 40 + KEYLEN
587 #define CSFUNC_ARGS user, channel, argc, argv, cmd
589 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
590 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
591 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
592 reply("MSG_MISSING_PARAMS", argv[0]); \
596 DECLARE_LIST(dnrList
, struct do_not_register
*);
597 DEFINE_LIST(dnrList
, struct do_not_register
*)
599 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
601 struct userNode
*chanserv
;
604 extern struct string_list
*autojoin_channels
;
605 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
606 static struct log_type
*CS_LOG
;
607 struct adduserPending
* adduser_pendings
= NULL
;
608 unsigned int adduser_pendings_count
= 0;
609 unsigned long god_timeout
;
613 struct channelList support_channels
;
614 struct mod_chanmode default_modes
;
616 unsigned long db_backup_frequency
;
617 unsigned long channel_expire_frequency
;
618 unsigned long ban_timeout_frequency
;
619 unsigned long dnr_expire_frequency
;
622 unsigned int adjust_delay
;
623 long channel_expire_delay
;
624 unsigned int nodelete_level
;
626 unsigned int adjust_threshold
;
627 int join_flood_threshold
;
629 unsigned int greeting_length
;
630 unsigned int refresh_period
;
631 unsigned int giveownership_period
;
633 unsigned int max_owned
;
634 unsigned int max_chan_users
;
635 unsigned int max_chan_bans
; /* lamers */
636 unsigned int max_userinfo_length
;
637 unsigned int valid_channel_regex_set
: 1;
639 regex_t valid_channel_regex
;
641 struct string_list
*set_shows
;
642 struct string_list
*eightball
;
643 struct string_list
*old_ban_names
;
644 struct string_list
*wheel
;
646 const char *ctcp_short_ban_duration
;
647 const char *ctcp_long_ban_duration
;
649 const char *irc_operator_epithet
;
650 const char *network_helper_epithet
;
651 const char *support_helper_epithet
;
656 struct userNode
*user
;
657 struct userNode
*bot
;
658 struct chanNode
*channel
;
660 unsigned short lowest
;
661 unsigned short highest
;
662 struct userData
**users
;
663 struct helpfile_table table
;
666 enum note_access_type
668 NOTE_SET_CHANNEL_ACCESS
,
669 NOTE_SET_CHANNEL_SETTER
,
673 enum note_visible_type
676 NOTE_VIS_CHANNEL_USERS
,
680 struct io_fd
*socket_io_fd
;
681 extern struct cManagerNode cManager
;
685 enum note_access_type set_access_type
;
687 unsigned int min_opserv
;
688 unsigned short min_ulevel
;
690 enum note_visible_type visible_type
;
691 unsigned int max_length
;
698 struct note_type
*type
;
699 char setter
[NICKSERV_HANDLE_LEN
+1];
703 static unsigned int registered_channels
;
704 static unsigned int banCount
;
706 static const struct {
709 unsigned short level
;
711 } accessLevels
[] = { /* MUST be orderd less to most! */
712 { "pal", "Pal", UL_PEON
, '+' },
713 { "peon", "Peon", UL_PEON
, '+' },
714 { "halfop", "HalfOp", UL_HALFOP
, '%' },
715 { "op", "Op", UL_OP
, '@' },
716 { "manager", "Manager", UL_MANAGER
, '%' },
717 { "coowner", "Coowner", UL_COOWNER
, '*' },
718 { "owner", "Owner", UL_OWNER
, '!' },
719 { "helper", "BUG:", UL_HELPER
, 'X' }
722 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
723 static const struct {
726 unsigned short default_value
;
727 unsigned int old_idx
;
728 unsigned int old_flag
;
729 unsigned short flag_value
;
731 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
732 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
733 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
734 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
735 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
736 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
737 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
738 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
739 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
742 struct charOptionValues
{
745 } automodeValues
[] = {
746 { 'n', "CSMSG_AUTOMODE_NONE" },
747 { 'y', "CSMSG_AUTOMODE_NORMAL" },
748 { 'v', "CSMSG_AUTOMODE_VOICE" },
749 { 'h', "CSMSG_AUTOMODE_HOP" },
750 { 'o', "CSMSG_AUTOMODE_OP" },
751 { 'm', "CSMSG_AUTOMODE_MUTE" },
752 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
753 }, protectValues
[] = {
754 { 'a', "CSMSG_PROTECT_ALL" },
755 { 'e', "CSMSG_PROTECT_EQUAL" },
756 { 'l', "CSMSG_PROTECT_LOWER" },
757 { 'n', "CSMSG_PROTECT_NONE" }
759 { 'd', "CSMSG_TOYS_DISABLED" },
760 { 'n', "CSMSG_TOYS_PRIVATE" },
761 { 'p', "CSMSG_TOYS_PUBLIC" }
762 }, topicRefreshValues
[] = {
763 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
764 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
765 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
766 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
767 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
768 }, ctcpReactionValues
[] = {
769 { 'n', "CSMSG_CTCPREACTION_NONE" },
770 { 'k', "CSMSG_CTCPREACTION_KICK" },
771 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
772 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
773 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
774 }, banTimeoutValues
[] = {
775 { '0', "CSMSG_BANTIMEOUT_NONE" },
776 { '1', "CSMSG_BANTIMEOUT_10M" },
777 { '2', "CSMSG_BANTIMEOUT_2H" },
778 { '3', "CSMSG_BANTIMEOUT_4H" },
779 { '4', "CSMSG_BANTIMEOUT_1D" },
780 { '5', "CSMSG_BANTIMEOUT_1W" }
783 { 'n', "CSMSG_RESYNC_NEVER" },
784 { '1', "CSMSG_RESYNC_3_HOURS" },
785 { '2', "CSMSG_RESYNC_6_HOURS" },
786 { '3', "CSMSG_RESYNC_12_HOURS" },
787 { '4', "CSMSG_RESYNC_24_HOURS" }
790 static const struct {
794 unsigned int old_idx
;
796 struct charOptionValues
*values
;
798 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
799 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
800 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
801 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
802 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
803 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
804 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
807 struct userData
*helperList
;
808 struct chanData
*channelList
;
809 static struct module *chanserv_module
;
810 static unsigned int userCount
;
811 unsigned int chanserv_read_version
= 0; /* db version control */
813 #define CHANSERV_DB_VERSION 2
815 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
816 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
818 void sputsock(const char *text
, ...) PRINTF_LIKE(1, 2);
821 sputsock(const char *text
, ...)
827 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
829 va_start(arg_list
, text
);
830 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
832 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
834 log_replay(MAIN_LOG
, true, buffer
);
835 buffer
[pos
++] = '\n';
837 ioset_write(socket_io_fd
, buffer
, pos
);
841 user_level_from_name(const char *name
, unsigned short clamp_level
)
843 unsigned int level
= 0, ii
;
845 level
= strtoul(name
, NULL
, 10);
846 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
847 if(!irccasecmp(name
, accessLevels
[ii
].name
))
848 level
= accessLevels
[ii
].level
;
849 if(level
> clamp_level
)
855 user_level_name_from_level(int level
)
863 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
864 if(level
>= accessLevels
[ii
].level
)
865 highest
= accessLevels
[ii
].title
;
871 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
874 *minl
= strtoul(arg
, &sep
, 10);
882 *maxl
= strtoul(sep
+1, &sep
, 10);
890 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
892 struct userData
*uData
, **head
;
894 if(!channel
|| !handle
)
897 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
898 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
900 for(uData
= helperList
;
901 uData
&& uData
->handle
!= handle
;
902 uData
= uData
->next
);
906 uData
= calloc(1, sizeof(struct userData
));
907 uData
->handle
= handle
;
909 uData
->access
= UL_HELPER
;
915 uData
->next
= helperList
;
917 helperList
->prev
= uData
;
925 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
926 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
929 head
= &(channel
->users
);
932 if(uData
&& (uData
!= *head
))
934 /* Shuffle the user to the head of whatever list he was in. */
936 uData
->next
->prev
= uData
->prev
;
938 uData
->prev
->next
= uData
->next
;
944 (**head
).prev
= uData
;
951 /* Returns non-zero if user has at least the minimum access.
952 * exempt_owner is set when handling !set, so the owner can set things
955 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
957 struct userData
*uData
;
958 struct chanData
*cData
= channel
->channel_info
;
959 unsigned short minimum
= cData
->lvlOpts
[opt
];
962 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
965 if(minimum
<= uData
->access
)
967 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
972 /* Scan for other users authenticated to the same handle
973 still in the channel. If so, keep them listed as present.
975 user is optional, if not null, it skips checking that userNode
976 (for the handle_part function) */
978 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
982 if(IsSuspended(uData
->channel
)
983 || IsUserSuspended(uData
)
984 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
996 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, const char *text
, UNUSED_ARG(struct userNode
*bot
), UNUSED_ARG(unsigned int is_notice
))
998 unsigned int eflags
, argc
;
1000 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
1002 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
1003 if(!channel
->channel_info
1004 || IsSuspended(channel
->channel_info
)
1006 || !ircncasecmp(text
, "ACTION ", 7))
1008 /* We dont punish people we know -Rubin
1009 * * Figure out the minimum level needed to CTCP the channel *
1011 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
1014 /* If they are a user of the channel, they are exempt */
1015 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
1017 /* We need to enforce against them; do so. */
1019 argv
[0] = (char*)text
;
1020 argv
[1] = user
->nick
;
1022 if(GetUserMode(channel
, user
))
1023 eflags
|= ACTION_KICK
;
1024 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
1025 default: case 'n': return;
1027 eflags
|= ACTION_KICK
;
1030 eflags
|= ACTION_BAN
;
1033 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1034 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
1037 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1038 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
1041 argv
[argc
++] = bad_ctcp_reason
;
1042 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
1046 chanserv_create_note_type(const char *name
)
1048 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
1049 strcpy(ntype
->name
, name
);
1051 dict_insert(note_types
, ntype
->name
, ntype
);
1056 chanserv_deref_note_type(void *data
)
1058 struct note_type
*ntype
= data
;
1060 if(--ntype
->refs
> 0)
1066 chanserv_flush_note_type(struct note_type
*ntype
)
1068 struct chanData
*cData
;
1069 for(cData
= channelList
; cData
; cData
= cData
->next
)
1070 dict_remove(cData
->notes
, ntype
->name
);
1074 chanserv_truncate_notes(struct note_type
*ntype
)
1076 struct chanData
*cData
;
1078 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1080 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1081 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1084 if(strlen(note
->note
) <= ntype
->max_length
)
1086 dict_remove2(cData
->notes
, ntype
->name
, 1);
1087 note
= realloc(note
, size
);
1088 note
->note
[ntype
->max_length
] = 0;
1089 dict_insert(cData
->notes
, ntype
->name
, note
);
1093 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1095 static struct note
*
1096 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1099 unsigned int len
= strlen(text
);
1101 if(len
> type
->max_length
) len
= type
->max_length
;
1102 note
= calloc(1, sizeof(*note
) + len
);
1104 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1105 memcpy(note
->note
, text
, len
);
1106 note
->note
[len
] = 0;
1107 dict_insert(channel
->notes
, type
->name
, note
);
1113 chanserv_free_note(void *data
)
1115 struct note
*note
= data
;
1117 chanserv_deref_note_type(note
->type
);
1118 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1122 static MODCMD_FUNC(cmd_createnote
) {
1123 struct note_type
*ntype
;
1124 unsigned int arg
= 1, existed
= 0, max_length
;
1126 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1129 ntype
= chanserv_create_note_type(argv
[arg
]);
1130 if(!irccasecmp(argv
[++arg
], "privileged"))
1133 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1134 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1136 else if(!irccasecmp(argv
[arg
], "channel"))
1138 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1141 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1144 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1145 ntype
->set_access
.min_ulevel
= ulvl
;
1147 else if(!irccasecmp(argv
[arg
], "setter"))
1149 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1153 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1157 if(!irccasecmp(argv
[++arg
], "privileged"))
1158 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1159 else if(!irccasecmp(argv
[arg
], "channel_users"))
1160 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1161 else if(!irccasecmp(argv
[arg
], "all"))
1162 ntype
->visible_type
= NOTE_VIS_ALL
;
1164 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1168 if((arg
+1) >= argc
) {
1169 reply("MSG_MISSING_PARAMS", argv
[0]);
1172 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1173 if(max_length
< 20 || max_length
> 450)
1175 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1178 if(existed
&& (max_length
< ntype
->max_length
))
1180 ntype
->max_length
= max_length
;
1181 chanserv_truncate_notes(ntype
);
1183 ntype
->max_length
= max_length
;
1186 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1188 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1193 dict_remove(note_types
, ntype
->name
);
1197 static MODCMD_FUNC(cmd_removenote
) {
1198 struct note_type
*ntype
;
1201 ntype
= dict_find(note_types
, argv
[1], NULL
);
1202 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1205 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1212 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1215 chanserv_flush_note_type(ntype
);
1217 dict_remove(note_types
, argv
[1]);
1218 reply("CSMSG_NOTE_DELETED", argv
[1]);
1223 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1227 if(orig
->modes_set
& change
->modes_clear
)
1229 if(orig
->modes_clear
& change
->modes_set
)
1231 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1232 && strcmp(orig
->new_key
, change
->new_key
))
1234 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1235 && (orig
->new_limit
!= change
->new_limit
))
1240 static char max_length_text
[MAXLEN
+1][16];
1242 static struct helpfile_expansion
1243 chanserv_expand_variable(const char *variable
)
1245 struct helpfile_expansion exp
;
1247 if(!irccasecmp(variable
, "notes"))
1250 exp
.type
= HF_TABLE
;
1251 exp
.value
.table
.length
= 1;
1252 exp
.value
.table
.width
= 3;
1253 exp
.value
.table
.flags
= 0;
1254 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1255 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1256 exp
.value
.table
.contents
[0][0] = "Note Type";
1257 exp
.value
.table
.contents
[0][1] = "Visibility";
1258 exp
.value
.table
.contents
[0][2] = "Max Length";
1259 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1261 struct note_type
*ntype
= iter_data(it
);
1264 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1265 row
= exp
.value
.table
.length
++;
1266 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1267 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1268 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1269 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1271 if(!max_length_text
[ntype
->max_length
][0])
1272 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1273 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1278 exp
.type
= HF_STRING
;
1279 exp
.value
.str
= NULL
;
1283 static struct chanData
*
1284 register_channel(struct chanNode
*cNode
, char *registrar
)
1286 struct chanData
*channel
;
1287 enum levelOption lvlOpt
;
1288 enum charOption chOpt
;
1290 channel
= calloc(1, sizeof(struct chanData
));
1292 channel
->notes
= dict_new();
1293 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1295 channel
->registrar
= strdup(registrar
);
1296 channel
->registered
= now
;
1297 channel
->visited
= now
;
1298 channel
->limitAdjusted
= now
;
1299 channel
->ownerTransfer
= now
;
1300 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1301 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1302 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1303 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1304 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1306 channel
->prev
= NULL
;
1307 channel
->next
= channelList
;
1310 channelList
->prev
= channel
;
1311 channelList
= channel
;
1312 registered_channels
++;
1314 channel
->channel
= cNode
;
1316 cNode
->channel_info
= channel
;
1321 static struct userData
*
1322 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access_level
, time_t seen
, const char *info
, time_t accessexpiry
)
1324 struct userData
*ud
;
1326 if(access_level
> UL_OWNER
)
1329 ud
= calloc(1, sizeof(*ud
));
1330 ud
->channel
= channel
;
1331 ud
->handle
= handle
;
1333 ud
->access
= access_level
;
1334 ud
->info
= info
? strdup(info
) : NULL
;
1335 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1340 ud
->next
= channel
->users
;
1342 channel
->users
->prev
= ud
;
1343 channel
->users
= ud
;
1345 channel
->userCount
++;
1349 ud
->u_next
= ud
->handle
->channels
;
1351 ud
->u_next
->u_prev
= ud
;
1352 ud
->handle
->channels
= ud
;
1354 ud
->flags
= USER_FLAGS_DEFAULT
;
1358 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1361 chanserv_expire_tempuser(void *data
)
1363 struct userData
*uData
= data
;
1367 handle
= strdup(uData
->handle
->handle
);
1368 if (uData
->accessexpiry
> 0) {
1369 if (uData
->present
) {
1370 struct userNode
*user
, *next_un
= NULL
;
1371 struct handle_info
*hi
;
1373 hi
= get_handle_info(handle
);
1374 for (user
= hi
->users
; user
; user
= next_un
) {
1375 struct mod_chanmode
*change
;
1376 struct modeNode
*mn
;
1377 unsigned int count
= 0;
1379 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1380 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1381 next_un
= user
->next_authed
;
1385 change
= mod_chanmode_alloc(2);
1386 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1387 change
->args
[count
++].u
.member
= mn
;
1390 change
->argc
= count
;
1391 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1393 mod_chanmode_free(change
);
1394 next_un
= user
->next_authed
;
1397 del_channel_user(uData
, 1);
1403 chanserv_expire_tempclvl(void *data
)
1405 struct userData
*uData
= data
;
1409 handle
= strdup(uData
->handle
->handle
);
1410 if (uData
->clvlexpiry
> 0) {
1411 int changemodes
= 0;
1412 unsigned int mode
= 0;
1414 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1416 mode
= MODE_REMOVE
| MODE_CHANOP
;
1417 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1419 mode
= MODE_REMOVE
| MODE_HALFOP
;
1423 if (uData
->present
) {
1424 struct userNode
*user
, *next_un
= NULL
;
1425 struct handle_info
*hi
;
1427 hi
= get_handle_info(handle
);
1428 for (user
= hi
->users
; user
; user
= next_un
) {
1429 struct mod_chanmode
*change
;
1430 struct modeNode
*mn
;
1431 unsigned int count
= 0;
1433 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1434 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1435 next_un
= user
->next_authed
;
1439 if (changemodes
== 0) {
1440 next_un
= user
->next_authed
;
1444 change
= mod_chanmode_alloc(2);
1445 change
->args
[count
].mode
= mode
;
1446 change
->args
[count
++].u
.member
= mn
;
1449 change
->argc
= count
;
1450 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1452 mod_chanmode_free(change
);
1453 next_un
= user
->next_authed
;
1457 uData
->access
= uData
->lastaccess
;
1458 uData
->lastaccess
= 0;
1459 uData
->clvlexpiry
= 0;
1465 del_channel_user(struct userData
*user
, int do_gc
)
1467 struct chanData
*channel
= user
->channel
;
1469 channel
->userCount
--;
1472 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1473 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1476 user
->prev
->next
= user
->next
;
1478 channel
->users
= user
->next
;
1480 user
->next
->prev
= user
->prev
;
1483 user
->u_prev
->u_next
= user
->u_next
;
1485 user
->handle
->channels
= user
->u_next
;
1487 user
->u_next
->u_prev
= user
->u_prev
;
1491 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1492 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1493 unregister_channel(channel
, "lost all users.");
1497 static struct adduserPending
*
1498 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1500 struct adduserPending
*ap
;
1501 ap
= calloc(1,sizeof(struct adduserPending
));
1502 ap
->channel
= channel
;
1505 ap
->created
= time(NULL
);
1507 /* ap->prev defaults to NULL already.. */
1508 ap
->next
= adduser_pendings
;
1509 if(adduser_pendings
)
1510 adduser_pendings
->prev
= ap
;
1511 adduser_pendings
= ap
;
1512 adduser_pendings_count
++;
1517 del_adduser_pending(struct adduserPending
*ap
)
1520 ap
->prev
->next
= ap
->next
;
1522 adduser_pendings
= ap
->next
;
1525 ap
->next
->prev
= ap
->prev
;
1529 static void expire_adduser_pending();
1531 /* find_adduser_pending(channel, user) will find an arbitrary record
1532 * from user, channel, or user and channel.
1533 * if user or channel are NULL, they will match any records.
1535 static struct adduserPending
*
1536 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1538 struct adduserPending
*ap
;
1540 expire_adduser_pending(); /* why not here.. */
1542 if(!channel
&& !user
) /* 2 nulls matches all */
1543 return(adduser_pendings
);
1544 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1546 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1553 /* Remove all pendings for a user or channel
1555 * called in nickserv.c DelUser() and proto-* unregister_channel()
1558 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1560 struct adduserPending
*ap
;
1562 /* So this is a bit wastefull, i hate dealing with linked lists.
1563 * if its a problem we'll rewrite it right */
1564 while((ap
= find_adduser_pending(channel
, user
))) {
1565 del_adduser_pending(ap
);
1569 /* Called from nickserv.c cmd_auth after someone auths */
1571 process_adduser_pending(struct userNode
*user
)
1573 struct adduserPending
*ap
;
1574 if(!user
->handle_info
)
1575 return; /* not associated with an account */
1576 while((ap
= find_adduser_pending(NULL
, user
)))
1578 struct userData
*actee
;
1579 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1581 /* Already on the userlist. do nothing*/
1585 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1586 scan_user_presence(actee
, NULL
);
1588 del_adduser_pending(ap
);
1593 expire_adduser_pending()
1595 struct adduserPending
*ap
, *ap_next
;
1596 ap
= adduser_pendings
;
1599 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1601 ap_next
= ap
->next
; /* save next */
1602 del_adduser_pending(ap
); /* free and relink */
1603 ap
= ap_next
; /* advance */
1610 static void expire_ban(void *data
);
1613 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1616 unsigned int ii
, l1
, l2
;
1621 bd
= malloc(sizeof(struct banData
));
1623 bd
->channel
= channel
;
1625 bd
->triggered
= triggered
;
1626 bd
->expires
= expires
;
1628 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1630 extern const char *hidden_host_suffix
;
1631 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1635 l2
= strlen(old_name
);
1638 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1640 new_mask
= alloca(MAXLEN
);
1641 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1644 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1646 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1647 bd
->reason
= strdup(reason
);
1650 timeq_add(expires
, expire_ban
, bd
);
1653 bd
->next
= channel
->bans
; /* lamers */
1655 channel
->bans
->prev
= bd
;
1657 channel
->banCount
++;
1664 del_channel_ban(struct banData
*ban
)
1666 ban
->channel
->banCount
--;
1670 ban
->prev
->next
= ban
->next
;
1672 ban
->channel
->bans
= ban
->next
;
1675 ban
->next
->prev
= ban
->prev
;
1678 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1687 expire_ban(void *data
) /* lamer.. */
1689 struct banData
*bd
= data
;
1690 if(!IsSuspended(bd
->channel
))
1692 struct banList bans
;
1693 struct mod_chanmode change
;
1695 bans
= bd
->channel
->channel
->banlist
;
1696 mod_chanmode_init(&change
);
1697 for(ii
=0; ii
<bans
.used
; ii
++)
1699 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1702 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1703 change
.args
[0].u
.hostmask
= bd
->mask
;
1704 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1710 del_channel_ban(bd
);
1713 static void chanserv_expire_suspension(void *data
);
1716 unregister_channel(struct chanData
*channel
, const char *reason
)
1718 struct mod_chanmode change
;
1719 char msgbuf
[MAXLEN
];
1721 /* After channel unregistration, the following must be cleaned
1723 - Channel information.
1725 - Channel bans. (lamers)
1726 - Channel suspension data.
1727 - adduser_pending data.
1728 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1734 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1738 mod_chanmode_init(&change
);
1739 change
.modes_clear
|= MODE_REGISTERED
;
1740 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1743 wipe_adduser_pending(channel
->channel
, NULL
);
1745 while(channel
->users
)
1746 del_channel_user(channel
->users
, 0);
1748 while(channel
->bans
)
1749 del_channel_ban(channel
->bans
);
1751 free(channel
->topic
);
1752 free(channel
->registrar
);
1753 free(channel
->greeting
);
1754 free(channel
->user_greeting
);
1755 free(channel
->topic_mask
);
1758 channel
->prev
->next
= channel
->next
;
1760 channelList
= channel
->next
;
1763 channel
->next
->prev
= channel
->prev
;
1765 if(channel
->suspended
)
1767 struct chanNode
*cNode
= channel
->channel
;
1768 struct suspended
*suspended
, *next_suspended
;
1770 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1772 next_suspended
= suspended
->previous
;
1773 free(suspended
->suspender
);
1774 free(suspended
->reason
);
1775 if(suspended
->expires
)
1776 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1781 cNode
->channel_info
= NULL
;
1783 channel
->channel
->channel_info
= NULL
;
1785 dict_delete(channel
->notes
);
1786 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1787 if(!IsSuspended(channel
))
1788 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1789 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1790 UnlockChannel(channel
->channel
);
1792 registered_channels
--;
1796 expire_channels(UNUSED_ARG(void *data
))
1798 struct chanData
*channel
, *next
;
1799 struct userData
*user
;
1800 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1802 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1803 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1805 for(channel
= channelList
; channel
; channel
= next
)
1807 next
= channel
->next
;
1809 /* See if the channel can be expired. */
1810 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1811 || IsProtected(channel
))
1814 /* Make sure there are no high-ranking users still in the channel. */
1815 for(user
=channel
->users
; user
; user
=user
->next
)
1816 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1821 /* Unregister the channel */
1822 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1823 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1824 unregister_channel(channel
, "registration expired.");
1827 if(chanserv_conf
.channel_expire_frequency
)
1828 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1832 expire_dnrs(UNUSED_ARG(void *data
))
1834 dict_iterator_t it
, next
;
1835 struct do_not_register
*dnr
;
1837 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
1839 dnr
= iter_data(it
);
1840 next
= iter_next(it
);
1841 if(dnr
->expires
&& dnr
->expires
<= now
)
1842 dict_remove(handle_dnrs
, dnr
->chan_name
+ 1);
1844 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
1846 dnr
= iter_data(it
);
1847 next
= iter_next(it
);
1848 if(dnr
->expires
&& dnr
->expires
<= now
)
1849 dict_remove(plain_dnrs
, dnr
->chan_name
+ 1);
1851 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
1853 dnr
= iter_data(it
);
1854 next
= iter_next(it
);
1855 if(dnr
->expires
&& dnr
->expires
<= now
)
1856 dict_remove(mask_dnrs
, dnr
->chan_name
+ 1);
1859 if(chanserv_conf
.dnr_expire_frequency
)
1860 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
1864 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1866 char protect
= channel
->chOpts
[chProtect
];
1867 struct userData
*cs_victim
, *cs_aggressor
;
1869 /* If victim access level is greater than set invitelevel, don't let
1870 * us kick them, but don't consider it punishment if someone else does
1874 if(victim
== aggressor
)
1876 /* Don't protect if the victim isn't authenticated (because they
1877 can't be a channel user), unless we are to protect non-users
1880 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1882 /* If they have enough access to invite themselvs through a ban,
1883 * and its us kicking them, don't. -Rubin */
1884 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1890 if(protect
!= 'a' && !cs_victim
)
1893 /* Protect if the aggressor isn't a user because at this point,
1894 the aggressor can only be less than or equal to the victim. */
1896 /* Not protected from chanserv except above */
1897 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1898 if(aggressor
== chanserv
)
1901 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1905 /* If the aggressor was a user, then the victim can't be helped. */
1912 if(cs_victim
->access
> cs_aggressor
->access
)
1917 if(cs_victim
->access
>= cs_aggressor
->access
)
1926 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1928 struct chanData
*cData
= channel
->channel_info
;
1929 struct userData
*cs_victim
;
1931 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1932 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1933 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1936 reply("CSMSG_OPBY_LOCKED");
1938 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1946 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1948 struct chanData
*cData
= channel
->channel_info
;
1949 struct userData
*cs_victim
;
1951 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1952 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1953 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1955 reply("CSMSG_HOPBY_LOCKED");
1964 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1966 if(IsService(victim
))
1968 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1972 if(protect_user(victim
, user
, channel
->channel_info
, false))
1974 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1982 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1984 if(IsService(victim
))
1986 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1990 if(protect_user(victim
, user
, channel
->channel_info
, false))
1992 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1999 static struct do_not_register
*
2000 chanserv_add_dnr(const char *chan_name
, const char *setter
, time_t expires
, const char *reason
)
2002 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
2003 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
2004 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
2005 strcpy(dnr
->reason
, reason
);
2007 dnr
->expires
= expires
;
2008 if(dnr
->chan_name
[0] == '*')
2009 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
2010 else if(strpbrk(dnr
->chan_name
, "*?"))
2011 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
2013 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
2017 static struct dnrList
2018 chanserv_find_dnrs(const char *chan_name
, const char *handle
, unsigned int max
)
2020 struct dnrList list
;
2021 dict_iterator_t it
, next
;
2022 struct do_not_register
*dnr
;
2024 dnrList_init(&list
);
2026 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
2028 if(dnr
->expires
&& dnr
->expires
<= now
)
2029 dict_remove(handle_dnrs
, handle
);
2030 else if (list
.used
< max
)
2031 dnrList_append(&list
, dnr
);
2034 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
2036 if(dnr
->expires
&& dnr
->expires
<= now
)
2037 dict_remove(plain_dnrs
, chan_name
);
2038 else if (list
.used
< max
)
2039 dnrList_append(&list
, dnr
);
2043 for(it
= dict_first(mask_dnrs
); it
&& list
.used
< max
; it
= next
)
2045 next
= iter_next(it
);
2046 if(!match_ircglob(chan_name
, iter_key(it
)))
2048 dnr
= iter_data(it
);
2049 if(dnr
->expires
&& dnr
->expires
<= now
)
2050 dict_remove(mask_dnrs
, iter_key(it
));
2052 dnrList_append(&list
, dnr
);
2058 static int dnr_print_func(struct do_not_register
*dnr
, void *extra
)
2060 struct userNode
*user
;
2061 char buf1
[INTERVALLEN
];
2062 char buf2
[INTERVALLEN
];
2066 strftime(buf1
, sizeof(buf1
), "%d %b %Y", localtime(&dnr
->set
));
2069 strftime(buf2
, sizeof(buf2
), "%d %b %Y", localtime(&dnr
->expires
));
2070 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET_EXPIRES", dnr
->chan_name
, buf1
, dnr
->setter
, buf2
, dnr
->reason
);
2074 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf1
, dnr
->setter
, dnr
->reason
);
2077 send_message(user
, chanserv
, "CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2082 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
2084 struct dnrList list
;
2087 list
= chanserv_find_dnrs(chan_name
, handle
, UINT_MAX
);
2088 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
2089 dnr_print_func(list
.list
[ii
], user
);
2091 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2096 struct do_not_register
*
2097 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2099 struct dnrList list
;
2100 struct do_not_register
*dnr
;
2102 list
= chanserv_find_dnrs(chan_name
, handle
? handle
->handle
: NULL
, 1);
2103 dnr
= list
.used
? list
.list
[0] : NULL
;
2108 static unsigned int send_dnrs(struct userNode
*user
, dict_t dict
)
2110 struct do_not_register
*dnr
;
2111 dict_iterator_t it
, next
;
2112 unsigned int matches
= 0;
2114 for(it
= dict_first(dict
); it
; it
= next
)
2116 dnr
= iter_data(it
);
2117 next
= iter_next(it
);
2118 if(dnr
->expires
&& dnr
->expires
<= now
)
2120 dict_remove(dict
, iter_key(it
));
2123 dnr_print_func(dnr
, user
);
2130 static CHANSERV_FUNC(cmd_noregister
)
2134 time_t expiry
, duration
;
2135 unsigned int matches
;
2139 reply("CSMSG_DNR_SEARCH_RESULTS");
2140 matches
= send_dnrs(user
, handle_dnrs
);
2141 matches
+= send_dnrs(user
, plain_dnrs
);
2142 matches
+= send_dnrs(user
, mask_dnrs
);
2144 reply("MSG_MATCH_COUNT", matches
);
2146 reply("MSG_NO_MATCHES");
2152 if(!IsChannelName(target
) && (*target
!= '*'))
2154 reply("CSMSG_NOT_DNR", target
);
2162 reply("MSG_INVALID_DURATION", argv
[2]);
2166 if(!strcmp(argv
[2], "0"))
2168 else if((duration
= ParseInterval(argv
[2])))
2169 expiry
= now
+ duration
;
2172 reply("MSG_INVALID_DURATION", argv
[2]);
2176 reason
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
2178 if((*target
== '*') && !get_handle_info(target
+ 1))
2180 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2183 chanserv_add_dnr(target
, user
->handle_info
->handle
, expiry
, reason
);
2184 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2188 reply("CSMSG_DNR_SEARCH_RESULTS");
2189 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2192 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2194 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2196 reply("MSG_NO_MATCHES");
2200 static CHANSERV_FUNC(cmd_allowregister
)
2202 const char *chan_name
= argv
[1];
2204 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2205 || dict_remove(plain_dnrs
, chan_name
)
2206 || dict_remove(mask_dnrs
, chan_name
))
2208 reply("CSMSG_DNR_REMOVED", chan_name
);
2211 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2216 struct userNode
*source
;
2220 time_t min_set
, max_set
;
2221 time_t min_expires
, max_expires
;
2226 dnr_search_matches(const struct do_not_register
*dnr
, const struct dnr_search
*search
)
2228 return !((dnr
->set
< search
->min_set
)
2229 || (dnr
->set
> search
->max_set
)
2230 || (dnr
->expires
< search
->min_expires
)
2231 || (search
->max_expires
2232 && ((dnr
->expires
== 0)
2233 || (dnr
->expires
> search
->max_expires
)))
2234 || (search
->chan_mask
2235 && !match_ircglob(dnr
->chan_name
, search
->chan_mask
))
2236 || (search
->setter_mask
2237 && !match_ircglob(dnr
->setter
, search
->setter_mask
))
2238 || (search
->reason_mask
2239 && !match_ircglob(dnr
->reason
, search
->reason_mask
)));
2242 static struct dnr_search
*
2243 dnr_search_create(struct userNode
*user
, struct svccmd
*cmd
, unsigned int argc
, char *argv
[])
2245 struct dnr_search
*discrim
;
2248 discrim
= calloc(1, sizeof(*discrim
));
2249 discrim
->source
= user
;
2250 discrim
->chan_mask
= NULL
;
2251 discrim
->setter_mask
= NULL
;
2252 discrim
->reason_mask
= NULL
;
2253 discrim
->max_set
= INT_MAX
;
2254 discrim
->limit
= 50;
2256 for(ii
=0; ii
<argc
; ++ii
)
2260 reply("MSG_MISSING_PARAMS", argv
[ii
]);
2263 else if(0 == irccasecmp(argv
[ii
], "channel"))
2265 discrim
->chan_mask
= argv
[++ii
];
2267 else if(0 == irccasecmp(argv
[ii
], "setter"))
2269 discrim
->setter_mask
= argv
[++ii
];
2271 else if(0 == irccasecmp(argv
[ii
], "reason"))
2273 discrim
->reason_mask
= argv
[++ii
];
2275 else if(0 == irccasecmp(argv
[ii
], "limit"))
2277 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 0);
2279 else if(0 == irccasecmp(argv
[ii
], "set"))
2281 const char *cmp
= argv
[++ii
];
2284 discrim
->min_set
= now
- ParseInterval(cmp
+ 2);
2286 discrim
->min_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2287 } else if(cmp
[0] == '=') {
2288 discrim
->min_set
= discrim
->max_set
= now
- ParseInterval(cmp
+ 1);
2289 } else if(cmp
[0] == '>') {
2291 discrim
->max_set
= now
- ParseInterval(cmp
+ 2);
2293 discrim
->max_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2295 discrim
->max_set
= now
- (ParseInterval(cmp
) - 1);
2298 else if(0 == irccasecmp(argv
[ii
], "expires"))
2300 const char *cmp
= argv
[++ii
];
2303 discrim
->max_expires
= now
+ ParseInterval(cmp
+ 2);
2305 discrim
->max_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2306 } else if(cmp
[0] == '=') {
2307 discrim
->min_expires
= discrim
->max_expires
= now
+ ParseInterval(cmp
+ 1);
2308 } else if(cmp
[0] == '>') {
2310 discrim
->min_expires
= now
+ ParseInterval(cmp
+ 2);
2312 discrim
->min_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2314 discrim
->min_expires
= now
+ (ParseInterval(cmp
) - 1);
2319 reply("MSG_INVALID_CRITERIA", argv
[ii
]);
2330 typedef int (*dnr_search_func
)(struct do_not_register
*match
, void *extra
);
2333 dnr_search(struct dnr_search
*discrim
, dnr_search_func dsf
, void *data
)
2335 struct do_not_register
*dnr
;
2336 dict_iterator_t next
;
2341 /* Initialize local variables. */
2344 if(discrim
->chan_mask
)
2346 int shift
= (discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*') ? 2 : 0;
2347 if('\0' == discrim
->chan_mask
[shift
+ strcspn(discrim
->chan_mask
+shift
, "*?")])
2351 if(target_fixed
&& discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*')
2353 /* Check against account-based DNRs. */
2354 dnr
= dict_find(handle_dnrs
, discrim
->chan_mask
+ 2, NULL
);
2355 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2358 else if(target_fixed
)
2360 /* Check against channel-based DNRs. */
2361 dnr
= dict_find(plain_dnrs
, discrim
->chan_mask
, NULL
);
2362 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2367 /* Exhaustively search account DNRs. */
2368 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
2370 next
= iter_next(it
);
2371 dnr
= iter_data(it
);
2372 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2376 /* Do the same for channel DNRs. */
2377 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
2379 next
= iter_next(it
);
2380 dnr
= iter_data(it
);
2381 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2385 /* Do the same for wildcarded channel DNRs. */
2386 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
2388 next
= iter_next(it
);
2389 dnr
= iter_data(it
);
2390 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2398 dnr_remove_func(struct do_not_register
*match
, void *extra
)
2400 struct userNode
*user
;
2403 chan_name
= alloca(strlen(match
->chan_name
) + 1);
2404 strcpy(chan_name
, match
->chan_name
);
2406 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2407 || dict_remove(plain_dnrs
, chan_name
)
2408 || dict_remove(mask_dnrs
, chan_name
))
2410 send_message(user
, chanserv
, "CSMSG_DNR_REMOVED", chan_name
);
2416 dnr_count_func(struct do_not_register
*match
, void *extra
)
2418 return 0; (void)match
; (void)extra
;
2421 static MODCMD_FUNC(cmd_dnrsearch
)
2423 struct dnr_search
*discrim
;
2424 dnr_search_func action
;
2425 struct svccmd
*subcmd
;
2426 unsigned int matches
;
2429 sprintf(buf
, "dnrsearch %s", argv
[1]);
2430 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
2433 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2436 if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
2438 if(!irccasecmp(argv
[1], "print"))
2439 action
= dnr_print_func
;
2440 else if(!irccasecmp(argv
[1], "remove"))
2441 action
= dnr_remove_func
;
2442 else if(!irccasecmp(argv
[1], "count"))
2443 action
= dnr_count_func
;
2446 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2450 discrim
= dnr_search_create(user
, cmd
, argc
-2, argv
+2);
2454 if(action
== dnr_print_func
)
2455 reply("CSMSG_DNR_SEARCH_RESULTS");
2456 matches
= dnr_search(discrim
, action
, user
);
2458 reply("MSG_MATCH_COUNT", matches
);
2460 reply("MSG_NO_MATCHES");
2466 chanserv_get_owned_count(struct handle_info
*hi
)
2468 struct userData
*cList
;
2471 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2472 if(cList
->access
== UL_OWNER
)
2477 static CHANSERV_FUNC(cmd_register
)
2479 struct handle_info
*handle
;
2480 struct chanData
*cData
;
2481 struct modeNode
*mn
;
2482 char reason
[MAXLEN
];
2484 unsigned int new_channel
, force
=0;
2485 struct do_not_register
*dnr
;
2488 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2489 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2495 if(channel
->channel_info
)
2497 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2501 if(channel
->bad_channel
)
2503 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2507 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2509 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2514 chan_name
= channel
->name
;
2520 reply("MSG_MISSING_PARAMS", cmd
->name
);
2521 svccmd_send_help_brief(user
, chanserv
, cmd
);
2524 if(!IsChannelName(argv
[1]))
2526 reply("MSG_NOT_CHANNEL_NAME");
2530 if(opserv_bad_channel(argv
[1]))
2532 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2537 chan_name
= argv
[1];
2540 if(argc
>= (new_channel
+2))
2542 if(!IsHelping(user
))
2544 reply("CSMSG_PROXY_FORBIDDEN");
2548 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2550 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2551 dnr
= chanserv_is_dnr(chan_name
, handle
);
2553 /* Check if they are over the limit.. */
2554 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2556 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2563 handle
= user
->handle_info
;
2564 dnr
= chanserv_is_dnr(chan_name
, handle
);
2565 /* Check if they are over the limit.. */
2566 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2568 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2571 /* Check if another service is in the channel */
2573 for(n
= 0; n
< channel
->members
.used
; n
++)
2575 mn
= channel
->members
.list
[n
];
2576 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2578 reply("CSMSG_ANOTHER_SERVICE");
2585 if(!IsHelping(user
))
2586 reply("CSMSG_DNR_CHANNEL", chan_name
);
2588 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2592 /* now handled above for message specilization *
2593 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2595 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2600 if (chanserv_conf
.valid_channel_regex_set
) {
2601 int err
= regexec(&chanserv_conf
.valid_channel_regex
, chan_name
, 0, 0, 0);
2604 buff
[regerror(err
, &chanserv_conf
.valid_channel_regex
, buff
, sizeof(buff
))] = 0;
2605 log_module(CS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
2607 if(err
== REG_NOMATCH
) {
2608 reply("CSMSG_ILLEGAL_CHANNEL", chan_name
);
2614 channel
= AddChannel(chan_name
, now
, NULL
, NULL
, NULL
);
2616 cData
= register_channel(channel
, user
->handle_info
->handle
);
2617 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2618 cData
->modes
= chanserv_conf
.default_modes
;
2620 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2621 if (IsOffChannel(cData
))
2623 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2627 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2628 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2629 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2631 mod_chanmode_announce(chanserv
, channel
, change
);
2632 mod_chanmode_free(change
);
2635 /* Initialize the channel's max user record. */
2636 cData
->max
= channel
->members
.used
;
2637 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2639 if(handle
!= user
->handle_info
)
2640 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2643 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2644 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2645 handle
->handle
, user
->handle_info
->handle
);
2650 make_confirmation_string(struct userData
*uData
)
2652 static char strbuf
[16];
2657 for(src
= uData
->handle
->handle
; *src
; )
2658 accum
= accum
* 31 + toupper(*src
++);
2660 for(src
= uData
->channel
->channel
->name
; *src
; )
2661 accum
= accum
* 31 + toupper(*src
++);
2662 sprintf(strbuf
, "%08x", accum
);
2666 static CHANSERV_FUNC(cmd_unregister
)
2669 char reason
[MAXLEN
];
2670 struct chanData
*cData
;
2671 struct userData
*uData
;
2673 cData
= channel
->channel_info
;
2676 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2680 uData
= GetChannelUser(cData
, user
->handle_info
);
2681 if(!uData
|| (uData
->access
< UL_OWNER
))
2683 reply("CSMSG_NO_ACCESS");
2687 if(IsProtected(cData
))
2689 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2693 if(!IsHelping(user
))
2695 const char *confirm_string
;
2696 if(IsSuspended(cData
))
2698 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2701 confirm_string
= make_confirmation_string(uData
);
2702 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2704 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2709 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2710 name
= strdup(channel
->name
);
2711 unregister_channel(cData
, reason
);
2712 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2713 reply("CSMSG_UNREG_SUCCESS", name
);
2719 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2721 extern struct userNode
*spamserv
;
2722 struct mod_chanmode
*change
;
2724 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2726 change
= mod_chanmode_alloc(2);
2728 change
->args
[0].mode
= MODE_CHANOP
;
2729 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2730 change
->args
[1].mode
= MODE_CHANOP
;
2731 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2735 change
= mod_chanmode_alloc(1);
2737 change
->args
[0].mode
= MODE_CHANOP
;
2738 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2741 mod_chanmode_announce(chanserv
, channel
, change
);
2742 mod_chanmode_free(change
);
2745 static CHANSERV_FUNC(cmd_move
)
2747 struct mod_chanmode change
;
2748 struct chanNode
*target
;
2749 struct modeNode
*mn
;
2750 struct userData
*uData
;
2751 struct do_not_register
*dnr
;
2752 int chanserv_join
= 0, spamserv_join
;
2756 if(IsProtected(channel
->channel_info
))
2758 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2762 if(!IsChannelName(argv
[1]))
2764 reply("MSG_NOT_CHANNEL_NAME");
2768 if(opserv_bad_channel(argv
[1]))
2770 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2774 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2776 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2778 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2780 if(!IsHelping(user
))
2781 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2783 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2789 mod_chanmode_init(&change
);
2790 if(!(target
= GetChannel(argv
[1])))
2792 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2793 if(!IsSuspended(channel
->channel_info
))
2796 else if(target
->channel_info
)
2798 reply("CSMSG_ALREADY_REGGED", target
->name
);
2801 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2802 && !IsHelping(user
))
2804 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2807 else if(!IsSuspended(channel
->channel_info
))
2812 /* Clear MODE_REGISTERED from old channel, add it to new. */
2814 change
.modes_clear
= MODE_REGISTERED
;
2815 mod_chanmode_announce(chanserv
, channel
, &change
);
2816 change
.modes_clear
= 0;
2817 change
.modes_set
= MODE_REGISTERED
;
2818 mod_chanmode_announce(chanserv
, target
, &change
);
2821 /* Move the channel_info to the target channel; it
2822 shouldn't be necessary to clear timeq callbacks
2823 for the old channel. */
2824 target
->channel_info
= channel
->channel_info
;
2825 target
->channel_info
->channel
= target
;
2826 channel
->channel_info
= NULL
;
2828 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2831 ss_cs_join_channel(target
, spamserv_join
);
2833 if(!IsSuspended(target
->channel_info
))
2835 char reason2
[MAXLEN
];
2836 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2837 DelChannelUser(chanserv
, channel
, reason2
, 0);
2840 UnlockChannel(channel
);
2841 LockChannel(target
);
2842 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2843 channel
->name
, target
->name
, user
->handle_info
->handle
);
2845 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2850 merge_users(struct chanData
*source
, struct chanData
*target
)
2852 struct userData
*suData
, *tuData
, *next
;
2858 /* Insert the source's users into the scratch area. */
2859 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2860 dict_insert(merge
, suData
->handle
->handle
, suData
);
2862 /* Iterate through the target's users, looking for
2863 users common to both channels. The lower access is
2864 removed from either the scratch area or target user
2866 for(tuData
= target
->users
; tuData
; tuData
= next
)
2868 struct userData
*choice
;
2870 next
= tuData
->next
;
2872 /* If a source user exists with the same handle as a target
2873 channel's user, resolve the conflict by removing one. */
2874 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2878 /* Pick the data we want to keep. */
2879 /* If the access is the same, use the later seen time. */
2880 if(suData
->access
== tuData
->access
)
2881 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2882 else /* Otherwise, keep the higher access level. */
2883 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2885 /* Remove the user that wasn't picked. */
2886 if(choice
== tuData
)
2888 dict_remove(merge
, suData
->handle
->handle
);
2889 del_channel_user(suData
, 0);
2892 del_channel_user(tuData
, 0);
2895 /* Move the remaining users to the target channel. */
2896 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2898 suData
= iter_data(it
);
2900 /* Insert the user into the target channel's linked list. */
2901 suData
->prev
= NULL
;
2902 suData
->next
= target
->users
;
2903 suData
->channel
= target
;
2906 target
->users
->prev
= suData
;
2907 target
->users
= suData
;
2909 /* Update the user counts for the target channel; the
2910 source counts are left alone. */
2911 target
->userCount
++;
2914 /* Possible to assert (source->users == NULL) here. */
2915 source
->users
= NULL
;
2920 merge_bans(struct chanData
*source
, struct chanData
*target
)
2922 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2924 /* Hold on to the original head of the target ban list
2925 to avoid comparing source bans with source bans. */
2926 tFront
= target
->bans
;
2928 /* Perform a totally expensive O(n*m) merge, ick. */
2929 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2931 /* Flag to track whether the ban's been moved
2932 to the destination yet. */
2935 /* Possible to assert (sbData->prev == NULL) here. */
2936 sNext
= sbData
->next
;
2938 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2940 tNext
= tbData
->next
;
2942 /* Perform two comparisons between each source
2943 and target ban, conflicts are resolved by
2944 keeping the broader ban and copying the later
2945 expiration and triggered time. */
2946 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2948 /* There is a broader ban in the target channel that
2949 overrides one in the source channel; remove the
2950 source ban and break. */
2951 if(sbData
->expires
> tbData
->expires
)
2952 tbData
->expires
= sbData
->expires
;
2953 if(sbData
->triggered
> tbData
->triggered
)
2954 tbData
->triggered
= sbData
->triggered
;
2955 del_channel_ban(sbData
);
2958 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2960 /* There is a broader ban in the source channel that
2961 overrides one in the target channel; remove the
2962 target ban, fall through and move the source over. */
2963 if(tbData
->expires
> sbData
->expires
)
2964 sbData
->expires
= tbData
->expires
;
2965 if(tbData
->triggered
> sbData
->triggered
)
2966 sbData
->triggered
= tbData
->triggered
;
2967 if(tbData
== tFront
)
2969 del_channel_ban(tbData
);
2972 /* Source bans can override multiple target bans, so
2973 we allow a source to run through this loop multiple
2974 times, but we can only move it once. */
2979 /* Remove the source ban from the source ban list. */
2981 sbData
->next
->prev
= sbData
->prev
;
2983 /* Modify the source ban's associated channel. */
2984 sbData
->channel
= target
;
2986 /* Insert the ban into the target channel's linked list. */
2987 sbData
->prev
= NULL
;
2988 sbData
->next
= target
->bans
;
2991 target
->bans
->prev
= sbData
;
2992 target
->bans
= sbData
;
2994 /* Update the user counts for the target channel. */
2999 /* Possible to assert (source->bans == NULL) here. */
3000 source
->bans
= NULL
;
3004 merge_data(struct chanData
*source
, struct chanData
*target
)
3006 /* Use more recent visited and owner-transfer time; use older
3007 * registered time. Bitwise or may_opchan. Use higher max.
3008 * Do not touch last_refresh, ban count or user counts.
3010 if(source
->visited
> target
->visited
)
3011 target
->visited
= source
->visited
;
3012 if(source
->registered
< target
->registered
)
3013 target
->registered
= source
->registered
;
3014 if(source
->ownerTransfer
> target
->ownerTransfer
)
3015 target
->ownerTransfer
= source
->ownerTransfer
;
3016 if(source
->may_opchan
)
3017 target
->may_opchan
= 1;
3018 if(source
->max
> target
->max
)
3019 target
->max
= source
->max
;
3023 merge_channel(struct chanData
*source
, struct chanData
*target
)
3025 merge_users(source
, target
);
3026 merge_bans(source
, target
);
3027 merge_data(source
, target
);
3030 static CHANSERV_FUNC(cmd_merge
)
3032 struct userData
*target_user
;
3033 struct chanNode
*target
;
3034 char reason
[MAXLEN
];
3039 /* Make sure the target channel exists and is registered to the user
3040 performing the command. */
3041 if(!(target
= GetChannel(argv
[1])))
3043 reply("MSG_INVALID_CHANNEL");
3048 if (!irccasecmp("nodelete", argv
[2]))
3052 if(!target
->channel_info
)
3054 reply("CSMSG_NOT_REGISTERED", target
->name
);
3058 if(IsProtected(channel
->channel_info
))
3060 reply("CSMSG_MERGE_NODELETE");
3064 if(IsSuspended(target
->channel_info
))
3066 reply("CSMSG_MERGE_SUSPENDED");
3070 if(channel
== target
)
3072 reply("CSMSG_MERGE_SELF");
3076 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
3077 if(!target_user
|| (target_user
->access
< UL_OWNER
))
3079 reply("CSMSG_MERGE_NOT_OWNER");
3083 /* Merge the channel structures and associated data. */
3084 merge_channel(channel
->channel_info
, target
->channel_info
);
3085 spamserv_cs_move_merge(user
, channel
, target
, 0);
3086 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
3088 unregister_channel(channel
->channel_info
, reason
);
3089 reply("CSMSG_MERGE_SUCCESS", target
->name
);
3093 static CHANSERV_FUNC(cmd_opchan
)
3095 struct mod_chanmode change
;
3096 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
3098 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
3101 if(!IsInChannel(channel
,chanserv
)) {
3102 reply("CSMSG_NOT_IN_CHANNEL", channel
->name
);
3105 channel
->channel_info
->may_opchan
= 0;
3106 mod_chanmode_init(&change
);
3108 change
.args
[0].mode
= MODE_CHANOP
;
3109 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
3110 if(!change
.args
[0].u
.member
)
3112 reply("CSMSG_OUT_OF_CHANNEL", channel
->name
);
3115 mod_chanmode_announce(chanserv
, channel
, &change
);
3116 reply("CSMSG_OPCHAN_DONE", channel
->name
);
3120 static CHANSERV_FUNC(cmd_adduser
)
3122 struct userData
*actee
;
3123 struct userData
*actor
, *real_actor
;
3124 struct handle_info
*handle
= NULL
;
3125 struct adduserPending
*tmp
;
3126 unsigned short access_level
, override
= 0;
3130 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
3132 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
3136 access_level
= user_level_from_name(argv
[2], UL_OWNER
);
3139 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3143 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3144 real_actor
= GetTrueChannelAccess(channel
->channel_info
, user
->handle_info
);
3146 if(actor
->access
<= access_level
)
3148 reply("CSMSG_NO_BUMP_ACCESS");
3152 /* Trying to add someone with equal/more access */
3153 if (!real_actor
|| real_actor
->access
<= access_level
)
3154 override
= CMD_LOG_OVERRIDE
;
3156 if((actee
= GetChannelAccess(channel
->channel_info
, handle
)))
3158 /* 'kevin must first authenticate with AuthServ'. is sent to user */
3159 struct userNode
*unode
;
3160 unode
= GetUserH(argv
[1]); /* find user struct by nick */
3163 if(find_adduser_pending(channel
, unode
)) {
3164 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
3167 if(IsInChannel(channel
, unode
)) {
3168 reply("CSMSG_ADDUSER_PENDING");
3169 tmp
= add_adduser_pending(channel
, unode
, access_level
);
3170 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
3172 /* this results in user must auth AND not in chan errors. too confusing..
3174 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3182 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3184 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
3188 time_t accessexpiry
= 0;
3189 unsigned int duration
= 0;
3191 if ((duration
= ParseInterval(argv
[3])))
3192 accessexpiry
= now
+ duration
;
3195 actee
= add_channel_user(channel
->channel_info
, handle
, access_level
, 0, NULL
, accessexpiry
);
3196 scan_user_presence(actee
, NULL
);
3199 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
3201 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access_level
), access
);
3202 return 1 | override
;
3205 static CHANSERV_FUNC(cmd_clvl
)
3207 struct handle_info
*handle
;
3208 struct userData
*victim
;
3209 struct userData
*actor
, *real_actor
;
3210 unsigned short new_access
, override
= 0;
3211 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3215 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3216 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3218 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3221 if(handle
== user
->handle_info
&& !privileged
)
3223 reply("CSMSG_NO_SELF_CLVL");
3227 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3229 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3233 if(actor
->access
<= victim
->access
&& !privileged
)
3235 reply("MSG_USER_OUTRANKED", handle
->handle
);
3239 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
3243 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3247 if(new_access
>= actor
->access
&& !privileged
)
3249 reply("CSMSG_NO_BUMP_ACCESS");
3253 time_t clvlexpiry
= 0;
3254 unsigned int duration
= 0;
3256 if ((duration
= ParseInterval(argv
[3])))
3257 clvlexpiry
= now
+ duration
;
3261 if (victim
->accessexpiry
> 0) {
3262 reply("CSMSG_NO_BUMP_EXPIRY");
3266 victim
->clvlexpiry
= clvlexpiry
;
3267 victim
->lastaccess
= victim
->access
;
3268 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
3271 /* Trying to clvl a equal/higher user */
3272 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& handle
!= user
->handle_info
))
3273 override
= CMD_LOG_OVERRIDE
;
3274 /* Trying to clvl someone to equal/higher access */
3275 if(!real_actor
|| new_access
>= real_actor
->access
)
3276 override
= CMD_LOG_OVERRIDE
;
3277 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3278 * If they lower their own access it's not a big problem.
3280 victim
->access
= new_access
;
3281 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
3282 return 1 | override
;
3285 static CHANSERV_FUNC(cmd_deluser
)
3287 struct handle_info
*handle
;
3288 struct userData
*victim
;
3289 struct userData
*actor
, *real_actor
;
3290 unsigned short access_level
, override
= 0;
3295 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3296 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3298 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
3301 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3303 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3309 access_level
= user_level_from_name(argv
[1], UL_OWNER
);
3310 char *useraccess
= user_level_name_from_level(victim
->access
);
3313 reply("CSMSG_INVALID_ACCESS", argv
[1]);
3316 if(strcasecmp(argv
[1], useraccess
))
3318 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
3324 access_level
= victim
->access
;
3327 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
3329 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
3333 /* If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override */
3334 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& real_actor
!= victim
))
3335 override
= CMD_LOG_OVERRIDE
;
3337 chan_name
= strdup(channel
->name
);
3338 del_channel_user(victim
, 1);
3339 reply("CSMSG_DELETED_USER", handle
->handle
, access_level
, chan_name
);
3341 return 1 | override
;
3345 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
3347 struct userData
*actor
, *real_actor
, *uData
, *next
;
3348 unsigned int override
= 0;
3350 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3351 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3353 if(min_access
> max_access
)
3355 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3359 if((actor
->access
<= max_access
) && !IsHelping(user
))
3361 reply("CSMSG_NO_ACCESS");
3365 if(!real_actor
|| real_actor
->access
<= max_access
)
3366 override
= CMD_LOG_OVERRIDE
;
3368 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3372 if((uData
->access
>= min_access
)
3373 && (uData
->access
<= max_access
)
3374 && match_ircglob(uData
->handle
->handle
, mask
))
3375 del_channel_user(uData
, 1);
3378 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3379 return 1 | override
;
3382 static CHANSERV_FUNC(cmd_mdelowner
)
3384 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3387 static CHANSERV_FUNC(cmd_mdelcoowner
)
3389 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3392 static CHANSERV_FUNC(cmd_mdelmanager
)
3394 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3397 static CHANSERV_FUNC(cmd_mdelop
)
3399 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3402 static CHANSERV_FUNC(cmd_mdelhalfop
)
3404 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3407 static CHANSERV_FUNC(cmd_mdelpeon
)
3409 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3412 static CHANSERV_FUNC(cmd_mdelpal
)
3414 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3417 static CHANSERV_FUNC(cmd_levels
)
3419 struct helpfile_table tbl
;
3422 tbl
.length
= 6 + 1; // 6 levels
3425 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3426 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3427 tbl
.contents
[0][0] = "Level";
3428 tbl
.contents
[0][1] = "From";
3429 tbl
.contents
[0][2] = "-";
3430 tbl
.contents
[0][3] = "To";
3432 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3433 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3434 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3435 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3436 tbl
.contents
[ii
][3] = msnprintf(1, "");
3438 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3439 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3440 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3441 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3442 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3444 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3445 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3446 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3447 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3448 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3450 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3451 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3452 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3453 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3454 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3456 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3457 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3458 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3459 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3460 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3462 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3463 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3464 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3465 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3466 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3468 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3472 reply("CSMSG_LEVELS_HEADER");
3473 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3474 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3475 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3476 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3477 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3478 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3485 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3487 struct banData
*bData
, *next
;
3488 char interval
[INTERVALLEN
];
3493 limit
= now
- duration
;
3494 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3498 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3501 del_channel_ban(bData
);
3505 intervalString(interval
, duration
, user
->handle_info
);
3506 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3511 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
)
3513 struct userData
*actor
, *uData
, *next
;
3514 char interval
[INTERVALLEN
];
3518 actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3519 if(min_access
> max_access
)
3521 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3525 if(!actor
|| actor
->access
<= max_access
)
3527 reply("CSMSG_NO_ACCESS");
3532 limit
= now
- duration
;
3533 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3537 if((uData
->seen
> limit
)
3539 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3542 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3543 || (!max_access
&& (uData
->access
< actor
->access
)))
3545 del_channel_user(uData
, 1);
3553 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3555 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3559 static CHANSERV_FUNC(cmd_trim
)
3561 unsigned long duration
;
3562 unsigned short min_level
, max_level
;
3567 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3568 duration
= ParseInterval(argv
[2]);
3571 reply("CSMSG_CANNOT_TRIM");
3575 if(!irccasecmp(argv
[1], "lamers"))
3577 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3580 else if(!irccasecmp(argv
[1], "users"))
3582 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3585 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3587 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3590 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3592 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3597 reply("CSMSG_INVALID_TRIM", argv
[1]);
3602 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3603 to the user. cmd_all takes advantage of this. */
3604 static CHANSERV_FUNC(cmd_up
)
3606 struct mod_chanmode change
;
3607 struct userData
*uData
;
3610 mod_chanmode_init(&change
);
3612 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3613 if(!change
.args
[0].u
.member
)
3616 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3620 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3624 reply("CSMSG_GODMODE_UP", argv
[0]);
3627 else if(uData
->access
>= UL_OP
)
3629 change
.args
[0].mode
= MODE_CHANOP
;
3630 errmsg
= "CSMSG_ALREADY_OPPED";
3632 else if(uData
->access
>= UL_HALFOP
)
3634 change
.args
[0].mode
= MODE_HALFOP
;
3635 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3637 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3639 change
.args
[0].mode
= MODE_VOICE
;
3640 errmsg
= "CSMSG_ALREADY_VOICED";
3645 reply("CSMSG_NO_ACCESS");
3648 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3649 if(!change
.args
[0].mode
)
3652 reply(errmsg
, channel
->name
);
3655 modcmd_chanmode_announce(&change
);
3659 static CHANSERV_FUNC(cmd_down
)
3661 struct mod_chanmode change
;
3663 mod_chanmode_init(&change
);
3665 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3666 if(!change
.args
[0].u
.member
)
3669 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3673 if(!change
.args
[0].u
.member
->modes
)
3676 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3680 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3681 modcmd_chanmode_announce(&change
);
3685 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
)
3687 struct userData
*cList
;
3689 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3691 if(IsSuspended(cList
->channel
)
3692 || IsUserSuspended(cList
)
3693 || !GetUserMode(cList
->channel
->channel
, user
))
3696 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3702 static CHANSERV_FUNC(cmd_upall
)
3704 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3707 static CHANSERV_FUNC(cmd_downall
)
3709 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3712 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3713 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3716 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
)
3718 unsigned int ii
, valid
;
3719 struct userNode
*victim
;
3720 struct mod_chanmode
*change
;
3722 change
= mod_chanmode_alloc(argc
- 1);
3724 for(ii
=valid
=0; ++ii
< argc
; )
3726 if(!(victim
= GetUserH(argv
[ii
])))
3728 change
->args
[valid
].mode
= mode
;
3729 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3730 if(!change
->args
[valid
].u
.member
)
3732 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3737 change
->argc
= valid
;
3738 if(valid
< (argc
-1))
3739 reply("CSMSG_PROCESS_FAILED");
3742 modcmd_chanmode_announce(change
);
3743 reply(action
, channel
->name
);
3745 mod_chanmode_free(change
);
3749 static CHANSERV_FUNC(cmd_op
)
3751 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3754 static CHANSERV_FUNC(cmd_hop
)
3756 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3759 static CHANSERV_FUNC(cmd_deop
)
3761 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3764 static CHANSERV_FUNC(cmd_dehop
)
3766 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3769 static CHANSERV_FUNC(cmd_voice
)
3771 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3774 static CHANSERV_FUNC(cmd_devoice
)
3776 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3780 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3786 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3788 struct modeNode
*mn
= channel
->members
.list
[ii
];
3790 if(IsService(mn
->user
))
3793 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3796 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3800 victims
[(*victimCount
)++] = mn
;
3806 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3808 struct userNode
*victim
;
3809 struct modeNode
**victims
;
3810 unsigned int offset
, n
, victimCount
, duration
= 0;
3811 char *reason
= "Bye.", *ban
, *name
;
3812 char interval
[INTERVALLEN
];
3814 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3815 REQUIRE_PARAMS(offset
);
3818 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3819 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3821 /* Truncate the reason to a length of TOPICLEN, as
3822 the ircd does; however, leave room for an ellipsis
3823 and the kicker's nick. */
3824 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3828 if((victim
= GetUserH(argv
[1])))
3830 victims
= alloca(sizeof(victims
[0]));
3831 victims
[0] = GetUserMode(channel
, victim
);
3832 /* XXX: The comparison with ACTION_KICK is just because all
3833 * other actions can work on users outside the channel, and we
3834 * want to allow those (e.g. unbans) in that case. If we add
3835 * some other ejection action for in-channel users, change
3837 victimCount
= victims
[0] ? 1 : 0;
3839 if(IsService(victim
))
3842 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3846 if((action
== ACTION_KICK
) && !victimCount
)
3849 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3853 if(protect_user(victim
, user
, channel
->channel_info
, false))
3855 // This translates to send_message(user, cmd->parent->bot, ...)
3856 // if user is x3 (ctcp action) cmd is null and segfault.
3858 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3862 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3863 name
= victim
->nick
;
3865 else if(!is_ircmask(argv
[1]) && (*argv
[1] == '*'))
3867 struct handle_info
*hi
;
3868 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
3869 const char *accountname
= argv
[1] + 1;
3871 if(!(hi
= get_handle_info(accountname
)))
3873 reply("MSG_HANDLE_UNKNOWN", accountname
);
3877 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", hi
->handle
);
3878 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3880 if(bad_channel_ban(channel
, user
, banmask
, &victimCount
, victims
))
3882 reply("CSMSG_MASK_PROTECTED", banmask
);
3886 if((action
== ACTION_KICK
) && (victimCount
== 0))
3888 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, banmask
);
3892 name
= ban
= strdup(banmask
);
3896 if(!is_ircmask(argv
[1]))
3899 reply("MSG_NICK_UNKNOWN", argv
[1]);
3903 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3905 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3908 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3911 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3912 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3914 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3915 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3916 some creativity, but its not x3's job to be the ban censor anyway. */
3917 if(is_overmask(argv
[1]))
3920 reply("CSMSG_LAME_MASK", argv
[1]);
3924 if((action
== ACTION_KICK
) && (victimCount
== 0))
3927 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3931 name
= ban
= strdup(argv
[1]);
3934 /* Truncate the ban in place if necessary; we must ensure
3935 that 'ban' is a valid ban mask before sanitizing it. */
3936 sanitize_ircmask(ban
);
3938 if(action
& ACTION_ADD_LAMER
)
3940 struct banData
*bData
, *next
;
3942 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3945 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3950 if(action
& ACTION_ADD_TIMED_LAMER
)
3952 duration
= ParseInterval(argv
[2]);
3957 reply("CSMSG_DURATION_TOO_LOW");
3961 else if(duration
> (86400 * 365 * 2))
3964 reply("CSMSG_DURATION_TOO_HIGH");
3971 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3973 if(match_ircglobs(bData
->mask
, ban
))
3975 int exact
= !irccasecmp(bData
->mask
, ban
);
3977 /* The ban is redundant; there is already a ban
3978 with the same effect in place. */
3982 free(bData
->reason
);
3983 bData
->reason
= strdup(reason
);
3984 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3986 reply("CSMSG_REASON_CHANGE", ban
);
3990 if(exact
&& bData
->expires
)
3994 /* If the ban matches an existing one exactly,
3995 extend the expiration time if the provided
3996 duration is longer. */
3997 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3999 bData
->expires
= now
+ duration
;
4010 /* Delete the expiration timeq entry and
4011 requeue if necessary. */
4012 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
4015 timeq_add(bData
->expires
, expire_ban
, bData
);
4019 /* automated kickban, dont reply */
4022 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
4024 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4030 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
4037 if(match_ircglobs(ban
, bData
->mask
))
4039 /* The ban we are adding makes previously existing
4040 bans redundant; silently remove them. */
4041 del_channel_ban(bData
);
4045 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
);
4047 name
= ban
= strdup(bData
->mask
);
4051 /* WHAT DOES THIS DO?? -Rubin */
4052 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
4054 extern const char *hidden_host_suffix
;
4055 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
4057 unsigned int l1
, l2
;
4060 l2
= strlen(old_name
);
4063 if(irccasecmp(ban
+ l1
- l2
, old_name
))
4065 new_mask
= malloc(MAXLEN
);
4066 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
4068 name
= ban
= new_mask
;
4073 if(action
& ACTION_BAN
)
4075 unsigned int exists
;
4076 struct mod_chanmode
*change
;
4078 if(channel
->banlist
.used
>= MAXBANS
)
4081 reply("CSMSG_BANLIST_FULL", channel
->name
);
4086 exists
= ChannelBanExists(channel
, ban
);
4087 change
= mod_chanmode_alloc(victimCount
+ 1);
4088 for(n
= 0; n
< victimCount
; ++n
)
4090 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
4091 change
->args
[n
].u
.member
= victims
[n
];
4095 change
->args
[n
].mode
= MODE_BAN
;
4096 change
->args
[n
++].u
.hostmask
= ban
;
4100 modcmd_chanmode_announce(change
);
4102 mod_chanmode_announce(chanserv
, channel
, change
);
4103 mod_chanmode_free(change
);
4105 if(exists
&& (action
== ACTION_BAN
))
4108 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
4114 if(action
& ACTION_ADD_LAMER
)
4116 char kick_reason
[MAXLEN
];
4117 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4119 for(n
= 0; n
< victimCount
; n
++) {
4120 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
4121 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4125 else if(action
& ACTION_KICK
)
4127 char kick_reason
[MAXLEN
];
4128 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4130 for(n
= 0; n
< victimCount
; n
++) {
4131 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4137 /* No response, since it was automated. */
4139 else if(action
& ACTION_ADD_LAMER
)
4142 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
4144 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4146 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
4147 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
4148 else if(action
& ACTION_BAN
)
4149 reply("CSMSG_BAN_DONE", name
, channel
->name
);
4150 else if(action
& ACTION_KICK
&& victimCount
)
4151 reply("CSMSG_KICK_DONE", name
, channel
->name
);
4157 static CHANSERV_FUNC(cmd_kickban
)
4159 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
4162 static CHANSERV_FUNC(cmd_kick
)
4164 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
4167 static CHANSERV_FUNC(cmd_ban
)
4169 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
4172 static CHANSERV_FUNC(cmd_addlamer
)
4174 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
4177 static CHANSERV_FUNC(cmd_addtimedlamer
)
4179 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
4182 static struct mod_chanmode
*
4183 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
4185 struct mod_chanmode
*change
;
4186 unsigned char *match
;
4187 unsigned int ii
, count
;
4189 match
= alloca(bans
->used
);
4192 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4194 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
4195 MATCH_USENICK
| MATCH_VISIBLE
);
4202 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4204 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
4211 change
= mod_chanmode_alloc(count
);
4212 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4216 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
4217 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
4219 assert(count
== change
->argc
);
4223 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
4225 unsigned int jj
, ii
, count
;
4227 struct chanData
*channel
;
4229 struct mod_chanmode
*change
;
4231 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
4232 /* Walk through every channel */
4233 for(channel
= channelList
; channel
; channel
= channel
->next
) {
4234 switch(channel
->chOpts
[chBanTimeout
])
4236 default: case '0': continue; /* Dont remove bans in this chan */
4237 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
4238 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
4239 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
4240 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
4241 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
4244 /* First find out how many bans were going to unset */
4245 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4246 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
4250 /* At least one ban, so setup a removal */
4251 change
= mod_chanmode_alloc(count
);
4253 /* Walk over every ban in this channel.. */
4254 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4255 bn
= channel
->channel
->banlist
.list
[jj
];
4256 if (bn
->set
< bantimeout
) {
4257 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
4259 /* Add this ban to the mode change */
4260 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4261 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
4263 /* Pull this ban out of the list */
4264 banList_remove(&(channel
->channel
->banlist
), bn
);
4269 /* Send the modes to IRC */
4270 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
4272 /* free memory from strdup above */
4273 for(ii
= 0; ii
< count
; ++ii
)
4274 free((char*)change
->args
[ii
].u
.hostmask
);
4276 mod_chanmode_free(change
);
4279 /* Set this function to run again */
4280 if(chanserv_conf
.ban_timeout_frequency
)
4281 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
4286 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
4288 struct userNode
*actee
;
4294 /* may want to allow a comma delimited list of users... */
4295 if(!(actee
= GetUserH(argv
[1])))
4297 if(!is_ircmask(argv
[1]) && *argv
[1] == '*')
4299 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
4300 const char *accountname
= argv
[1] + 1;
4302 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", accountname
);
4303 mask
= strdup(banmask
);
4305 else if(!is_ircmask(argv
[1]))
4307 reply("MSG_NICK_UNKNOWN", argv
[1]);
4312 mask
= strdup(argv
[1]);
4316 /* We don't sanitize the mask here because ircu
4318 if(action
& ACTION_UNBAN
)
4320 struct mod_chanmode
*change
;
4321 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
4326 modcmd_chanmode_announce(change
);
4327 for(ii
= 0; ii
< change
->argc
; ++ii
)
4328 free((char*)change
->args
[ii
].u
.hostmask
);
4329 mod_chanmode_free(change
);
4334 if(action
& ACTION_DEL_LAMER
)
4336 struct banData
*ban
, *next
;
4338 ban
= channel
->channel_info
->bans
; /* lamers */
4342 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
4345 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
4350 del_channel_ban(ban
);
4357 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
4359 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
4365 static CHANSERV_FUNC(cmd_unban
)
4367 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
4370 static CHANSERV_FUNC(cmd_dellamer
)
4372 /* it doesn't necessarily have to remove the channel ban - may want
4373 to make that an option. */
4374 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
4377 static CHANSERV_FUNC(cmd_unbanme
)
4379 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4380 long flags
= ACTION_UNBAN
;
4382 /* remove permanent bans if the user has the proper access. */
4383 if(uData
->access
>= UL_MANAGER
)
4384 flags
|= ACTION_DEL_LAMER
;
4386 argv
[1] = user
->nick
;
4387 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
4390 static CHANSERV_FUNC(cmd_unbanall
)
4392 struct mod_chanmode
*change
;
4395 if(!channel
->banlist
.used
)
4397 reply("CSMSG_NO_BANS", channel
->name
);
4401 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4402 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4404 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4405 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4407 modcmd_chanmode_announce(change
);
4408 for(ii
= 0; ii
< change
->argc
; ++ii
)
4409 free((char*)change
->args
[ii
].u
.hostmask
);
4410 mod_chanmode_free(change
);
4411 reply("CSMSG_BANS_REMOVED", channel
->name
);
4415 static CHANSERV_FUNC(cmd_open
)
4417 struct mod_chanmode
*change
;
4420 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4422 change
= mod_chanmode_alloc(0);
4423 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4424 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4425 && channel
->channel_info
->modes
.modes_set
)
4426 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4427 modcmd_chanmode_announce(change
);
4428 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4429 for(ii
= 0; ii
< change
->argc
; ++ii
)
4430 free((char*)change
->args
[ii
].u
.hostmask
);
4431 mod_chanmode_free(change
);
4435 static CHANSERV_FUNC(cmd_myaccess
)
4437 static struct string_buffer sbuf
;
4438 struct handle_info
*target_handle
;
4439 struct userData
*uData
;
4442 target_handle
= user
->handle_info
;
4443 else if(!IsStaff(user
))
4445 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4448 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4451 if(!oper_outranks(user
, target_handle
))
4454 if(!target_handle
->channels
)
4456 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4460 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4461 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4463 struct chanData
*cData
= uData
->channel
;
4465 if(uData
->access
> UL_OWNER
)
4467 if(IsProtected(cData
)
4468 && (target_handle
!= user
->handle_info
)
4469 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4472 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4473 if(uData
->flags
== USER_AUTO_OP
)
4474 string_buffer_append(&sbuf
, ',');
4475 if(IsUserSuspended(uData
))
4476 string_buffer_append(&sbuf
, 's');
4477 if(IsUserAutoOp(uData
))
4479 if(uData
->access
>= UL_OP
)
4480 string_buffer_append(&sbuf
, 'o');
4481 else if(uData
->access
>= UL_HALFOP
)
4482 string_buffer_append(&sbuf
, 'h');
4483 else if(uData
->access
>= UL_PEON
)
4484 string_buffer_append(&sbuf
, 'v');
4486 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4487 string_buffer_append(&sbuf
, 'i');
4488 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4489 string_buffer_append(&sbuf
, 'j');
4491 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4493 string_buffer_append_string(&sbuf
, ")]");
4494 string_buffer_append(&sbuf
, '\0');
4495 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4501 static CHANSERV_FUNC(cmd_access
)
4503 struct userNode
*target
;
4504 struct handle_info
*target_handle
;
4505 struct userData
*uData
;
4507 char prefix
[MAXLEN
];
4512 target_handle
= target
->handle_info
;
4514 else if((target
= GetUserH(argv
[1])))
4516 target_handle
= target
->handle_info
;
4518 else if(argv
[1][0] == '*')
4520 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4522 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4528 reply("MSG_NICK_UNKNOWN", argv
[1]);
4532 assert(target
|| target_handle
);
4534 if(target
== chanserv
)
4536 reply("CSMSG_IS_CHANSERV");
4544 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4549 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4552 reply("MSG_AUTHENTICATE");
4558 const char *epithet
= NULL
, *type
= NULL
;
4561 epithet
= chanserv_conf
.irc_operator_epithet
;
4562 type
= user_find_message(user
, "CSMSG_OPERATOR_TITLE");
4564 else if(IsNetworkHelper(target
))
4566 epithet
= chanserv_conf
.network_helper_epithet
;
4567 type
= user_find_message(user
, "CSMSG_UC_H_TITLE");
4569 else if(IsSupportHelper(target
))
4571 epithet
= chanserv_conf
.support_helper_epithet
;
4572 type
= user_find_message(user
, "CSMSG_LC_H_TITLE");
4576 if(target_handle
->epithet
)
4577 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4579 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4581 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4585 sprintf(prefix
, "%s", target_handle
->handle
);
4588 if(!channel
->channel_info
)
4590 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4594 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4595 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4596 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4598 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4599 /* To prevent possible information leaks, only show infolines
4600 * if the requestor is in the channel or it's their own
4602 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4604 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4606 /* Likewise, only say it's suspended if the user has active
4607 * access in that channel or it's their own entry. */
4608 if(IsUserSuspended(uData
)
4609 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4610 || (user
->handle_info
== uData
->handle
)))
4612 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4617 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4623 /* This is never used...
4625 zoot_list(struct listData *list)
4627 struct userData *uData;
4628 unsigned int start, curr, highest, lowest;
4629 struct helpfile_table tmp_table;
4630 const char **temp, *msg;
4632 if(list->table.length == 1)
4635 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);
4637 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));
4638 msg = user_find_message(list->user, "MSG_NONE");
4639 send_message_type(4, list->user, list->bot, " %s", msg);
4641 tmp_table.width = list->table.width;
4642 tmp_table.flags = list->table.flags;
4643 list->table.contents[0][0] = " ";
4644 highest = list->highest;
4645 if(list->lowest != 0)
4646 lowest = list->lowest;
4647 else if(highest < 100)
4650 lowest = highest - 100;
4651 for(start = curr = 1; curr < list->table.length; )
4653 uData = list->users[curr-1];
4654 list->table.contents[curr++][0] = " ";
4655 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4658 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);
4660 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));
4661 temp = list->table.contents[--start];
4662 list->table.contents[start] = list->table.contents[0];
4663 tmp_table.contents = list->table.contents + start;
4664 tmp_table.length = curr - start;
4665 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4666 list->table.contents[start] = temp;
4668 highest = lowest - 1;
4669 lowest = (highest < 100) ? 0 : (highest - 99);
4676 normal_list(struct listData
*list
)
4680 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
);
4682 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
));
4683 if(list
->table
.length
== 1)
4685 msg
= user_find_message(list
->user
, "MSG_NONE");
4686 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4689 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4692 /* if these need changed, uncomment and customize
4694 clean_list(struct listData *list)
4698 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);
4700 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));
4701 if(list->table.length == 1)
4703 msg = user_find_message(list->user, "MSG_NONE");
4704 send_message_type(4, list->user, list->bot, " %s", msg);
4707 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4711 advanced_list(struct listData *list)
4715 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);
4717 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));
4718 if(list->table.length == 1)
4720 msg = user_find_message(list->user, "MSG_NONE");
4721 send_message_type(4, list->user, list->bot, " %s", msg);
4724 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4728 classic_list(struct listData *list)
4732 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4734 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4735 if(list->table.length == 1)
4737 msg = user_find_message(list->user, "MSG_NONE");
4738 send_message_type(4, list->user, list->bot, " %s", msg);
4741 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4746 userData_access_comp(const void *arg_a
, const void *arg_b
)
4748 const struct userData
*a
= *(struct userData
**)arg_a
;
4749 const struct userData
*b
= *(struct userData
**)arg_b
;
4751 if(a
->access
!= b
->access
)
4752 res
= b
->access
- a
->access
;
4754 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4759 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4761 void (*send_list
)(struct listData
*);
4762 struct userData
*uData
;
4763 struct listData lData
;
4764 unsigned int matches
;
4770 lData
.bot
= cmd
->parent
->bot
;
4771 lData
.channel
= channel
;
4772 lData
.lowest
= lowest
;
4773 lData
.highest
= highest
;
4774 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4775 send_list
= normal_list
;
4776 /* What does the following line do exactly?? */
4777 /*(void)zoot_list; ** since it doesn't show user levels */
4780 if(user->handle_info)
4782 switch(user->handle_info->userlist_style)
4784 case HI_STYLE_CLEAN:
4785 send_list = clean_list;
4787 case HI_STYLE_ADVANCED:
4788 send_list = advanced_list;
4790 case HI_STYLE_CLASSIC:
4791 send_list = classic_list;
4793 case HI_STYLE_NORMAL:
4795 send_list = normal_list;
4800 send_list
= normal_list
;
4802 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4804 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4806 if((uData
->access
< lowest
)
4807 || (uData
->access
> highest
)
4808 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4810 lData
.users
[matches
++] = uData
;
4812 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4814 lData
.table
.length
= matches
+1;
4815 lData
.table
.flags
= TABLE_NO_FREE
;
4816 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4818 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4819 lData
.table
.width
= 6; /* with level = 6 */
4821 lData
.table
.width
= 5; /* without = 5 */
4822 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4823 lData
.table
.contents
[0] = ary
;
4824 if(user
->handle_info
) {
4825 switch(user
->handle_info
->userlist_style
) {
4826 case HI_STYLE_CLASSIC
:
4829 case HI_STYLE_ADVANCED
:
4830 ary
[i
++] = "Access";
4833 case HI_STYLE_CLEAN
:
4834 ary
[i
++] = "Access";
4836 case HI_STYLE_NORMAL
:
4838 ary
[i
++] = "Access";
4843 ary
[i
++] = "Access";
4845 ary
[i
++] = "Account";
4846 ary
[i
] = "Last Seen";
4848 ary
[i
++] = "Status";
4849 ary
[i
++] = "Expiry";
4850 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4852 char seen
[INTERVALLEN
];
4855 uData
= lData
.users
[matches
-1];
4857 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4858 lData
.table
.contents
[matches
] = ary
;
4859 if(user
->handle_info
) {
4860 switch(user
->handle_info
->userlist_style
) {
4861 case HI_STYLE_CLASSIC
:
4862 ary
[i
++] = strtab(uData
->access
);
4864 case HI_STYLE_ADVANCED
:
4865 ary
[i
++] = user_level_name_from_level(uData
->access
);
4866 ary
[i
++] = strtab(uData
->access
);
4868 case HI_STYLE_CLEAN
:
4869 ary
[i
++] = user_level_name_from_level(uData
->access
);
4871 case HI_STYLE_NORMAL
:
4873 ary
[i
++] = user_level_name_from_level(uData
->access
);
4878 ary
[i
++] = user_level_name_from_level(uData
->access
);
4880 ary
[i
++] = uData
->handle
->handle
;
4883 else if(!uData
->seen
)
4886 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4887 ary
[i
] = strdup(ary
[i
]);
4889 if(IsUserSuspended(uData
))
4890 ary
[i
++] = "Suspended";
4891 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4892 ary
[i
++] = "Vacation";
4894 ary
[i
++] = "Normal";
4896 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4897 char delay
[INTERVALLEN
];
4900 if (uData
->accessexpiry
> 0) {
4901 diff
= uData
->accessexpiry
- now
;
4902 intervalString(delay
, diff
, user
->handle_info
);
4904 diff
= uData
->clvlexpiry
- now
;
4905 intervalString(delay
, diff
, user
->handle_info
);
4913 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4915 /* Free strdup above */
4916 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4917 free(lData
.table
.contents
[matches
]);
4919 free(lData
.table
.contents
[0]);
4920 free(lData
.table
.contents
);
4924 /* Remove this now that debugging is over? or improve it for
4925 * users? Would it be better tied into USERS somehow? -Rubin */
4926 static CHANSERV_FUNC(cmd_pending
)
4928 struct adduserPending
*ap
;
4929 reply("CSMSG_ADDUSER_PENDING_HEADER");
4930 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4932 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4933 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4934 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4938 static CHANSERV_FUNC(cmd_users
)
4940 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4943 static CHANSERV_FUNC(cmd_wlist
)
4945 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4948 static CHANSERV_FUNC(cmd_clist
)
4950 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4953 static CHANSERV_FUNC(cmd_mlist
)
4955 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4958 static CHANSERV_FUNC(cmd_olist
)
4960 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4963 static CHANSERV_FUNC(cmd_hlist
)
4965 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4968 static CHANSERV_FUNC(cmd_plist
)
4970 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4973 static CHANSERV_FUNC(cmd_lamers
)
4975 struct userNode
*search_u
= NULL
;
4976 struct helpfile_table tbl
;
4977 unsigned int matches
= 0, timed
= 0, search_wilds
= 0, ii
;
4978 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4979 const char *msg_never
, *triggered
, *expires
;
4980 struct banData
*ban
, **bans
; /* lamers */
4984 else if(strchr(search
= argv
[1], '!'))
4987 search_wilds
= search
[strcspn(search
, "?*")];
4989 else if(!(search_u
= GetUserH(search
)))
4990 reply("MSG_NICK_UNKNOWN", search
);
4992 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4993 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4996 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
5000 if(!user_matches_glob(search_u
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
5005 if(search_wilds
? !match_ircglobs(search
, ban
->mask
) : !match_ircglob(search
, ban
->mask
))
5008 bans
[matches
++] = ban
;
5013 tbl
.length
= matches
+ 1;
5014 tbl
.width
= 4 + timed
;
5016 tbl
.flags
= TABLE_NO_FREE
;
5017 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
5018 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5019 tbl
.contents
[0][0] = "Mask";
5020 tbl
.contents
[0][1] = "Set By";
5021 tbl
.contents
[0][2] = "Triggered";
5024 tbl
.contents
[0][3] = "Expires";
5025 tbl
.contents
[0][4] = "Reason";
5028 tbl
.contents
[0][3] = "Reason";
5031 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5032 /* reply("MSG_NONE"); */
5033 free(tbl
.contents
[0]);
5038 msg_never
= user_find_message(user
, "MSG_NEVER");
5039 for(ii
= 0; ii
< matches
; )
5045 else if(ban
->expires
)
5046 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
5048 expires
= msg_never
;
5051 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
5053 triggered
= msg_never
;
5055 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5056 tbl
.contents
[ii
][0] = ban
->mask
;
5057 tbl
.contents
[ii
][1] = ban
->owner
;
5058 tbl
.contents
[ii
][2] = strdup(triggered
);
5061 tbl
.contents
[ii
][3] = strdup(expires
);
5062 tbl
.contents
[ii
][4] = ban
->reason
;
5065 tbl
.contents
[ii
][3] = ban
->reason
;
5067 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5068 /* reply("MSG_MATCH_COUNT", matches); */
5069 for(ii
= 1; ii
< tbl
.length
; ++ii
)
5071 free((char*)tbl
.contents
[ii
][2]);
5073 free((char*)tbl
.contents
[ii
][3]);
5074 free(tbl
.contents
[ii
]);
5076 free(tbl
.contents
[0]);
5083 * return + if the user does NOT have the right to set the topic, and
5084 * the topic is changed.
5087 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
5089 struct chanData
*cData
= channel
->channel_info
;
5090 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5092 else if(cData
->topic
)
5093 return irccasecmp(new_topic
, cData
->topic
);
5100 * Makes a givin topic fit into a givin topic mask and returns
5103 * topic_mask - the mask to conform to
5104 * topic - the topic to make conform
5105 * new_topic - the pre-allocated char* to put the new topic into
5107 * modifies: new_topic
5110 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
5112 //char *topic_mask = cData->topic_mask;
5114 int pos
=0, starpos
=-1, dpos
=0, len
;
5116 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
5123 strcpy(new_topic
, "");
5126 len
= strlen(topic
);
5127 if((dpos
+ len
) > TOPICLEN
)
5128 len
= TOPICLEN
+ 1 - dpos
;
5129 memcpy(new_topic
+dpos
, topic
, len
);
5133 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
5134 default: new_topic
[dpos
++] = tchar
; break;
5137 if((dpos
> TOPICLEN
) || tchar
)
5139 strcpy(new_topic
, "");
5142 new_topic
[dpos
] = 0;
5146 static CHANSERV_FUNC(cmd_topic
)
5148 struct chanData
*cData
;
5152 #ifdef WITH_PROTOCOL_P10
5156 cData
= channel
->channel_info
;
5161 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5162 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
5163 reply("CSMSG_TOPIC_SET", cData
->topic
);
5167 reply("CSMSG_NO_TOPIC", channel
->name
);
5171 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5172 /* If they say "!topic *", use an empty topic. */
5173 if((topic
[0] == '*') && (topic
[1] == 0))
5176 if(bad_topic(channel
, user
, topic
))
5178 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5183 /* If there is a topicmask set, and the new topic doesnt match, make it */
5184 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
5186 char *topic_mask
= cData
->topic_mask
;
5187 char new_topic
[TOPICLEN
+1];
5189 /* make a new topic fitting mask */
5190 conform_topic(topic_mask
, topic
, new_topic
);
5193 /* Topic couldnt fit into mask, was too long */
5194 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
5195 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
5198 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
5200 else /* No mask set, just set the topic */
5201 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
5204 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
5206 /* Grab the topic and save it as the default topic. */
5208 cData
->topic
= strdup(channel
->topic
);
5214 static CHANSERV_FUNC(cmd_mode
)
5216 struct userData
*uData
;
5217 struct mod_chanmode
*change
;
5222 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5223 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5227 change
= &channel
->channel_info
->modes
;
5228 if(change
->modes_set
|| change
->modes_clear
) {
5229 modcmd_chanmode_announce(change
);
5230 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
5232 reply("CSMSG_NO_MODES", channel
->name
);
5236 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5238 base_oplevel
= MAXOPLEVEL
;
5239 else if (uData
->access
>= UL_OWNER
)
5242 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
5243 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
5247 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
5251 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
5252 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
5255 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5256 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
5260 modcmd_chanmode_announce(change
);
5261 mod_chanmode_free(change
);
5262 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
5266 static CHANSERV_FUNC(cmd_invite
)
5268 struct userData
*uData
;
5269 struct userNode
*invite
;
5271 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5275 if(!(invite
= GetUserH(argv
[1])))
5277 reply("MSG_NICK_UNKNOWN", argv
[1]);
5284 if(GetUserMode(channel
, invite
))
5286 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
5294 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5295 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
5298 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
5301 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
5303 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
5304 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
5305 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
5311 irc_invite(chanserv
, invite
, channel
);
5313 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
5318 static CHANSERV_FUNC(cmd_inviteme
)
5320 if(GetUserMode(channel
, user
))
5322 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
5325 if(channel
->channel_info
5326 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
5328 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
5331 irc_invite(cmd
->parent
->bot
, user
, channel
);
5336 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
5339 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
5341 /* We display things based on two dimensions:
5342 * - Issue time: present or absent
5343 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5344 * (in order of precedence, so something both expired and revoked
5345 * only counts as revoked)
5347 combo
= (suspended
->issued
? 4 : 0)
5348 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
5350 case 0: /* no issue time, indefinite expiration */
5351 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
5353 case 1: /* no issue time, expires in future */
5354 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
5355 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
5357 case 2: /* no issue time, expired */
5358 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
5359 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
5361 case 3: /* no issue time, revoked */
5362 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
5363 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
5365 case 4: /* issue time set, indefinite expiration */
5366 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5367 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
5369 case 5: /* issue time set, expires in future */
5370 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5371 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
5372 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5374 case 6: /* issue time set, expired */
5375 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5376 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
5377 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5379 case 7: /* issue time set, revoked */
5380 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5381 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
5382 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5385 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
5391 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
5394 const char *fmt
= "%a %b %d %H:%M %Y";
5395 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
5397 if(giveownership
->staff_issuer
)
5399 if(giveownership
->reason
)
5400 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
5401 giveownership
->target
, giveownership
->target_access
,
5402 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
5404 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
5405 giveownership
->target
, giveownership
->target_access
,
5406 giveownership
->staff_issuer
, buf
);
5410 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5415 static CHANSERV_FUNC(cmd_info
)
5417 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5418 struct userData
*uData
, *owner
;
5419 struct chanData
*cData
;
5420 struct do_not_register
*dnr
;
5425 cData
= channel
->channel_info
;
5426 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5427 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5430 uData
= GetChannelUser(cData
, user
->handle_info
);
5431 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5433 mod_chanmode_format(&cData
->modes
, modes
);
5434 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5435 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5438 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5442 note
= iter_data(it
);
5443 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5446 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5447 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5450 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5451 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5452 if(owner
->access
== UL_OWNER
)
5453 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5454 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5455 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5456 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5458 privileged
= IsStaff(user
);
5459 /* if(privileged) */
5460 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5461 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5462 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5464 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5465 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5467 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5469 struct suspended
*suspended
;
5470 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5471 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5472 show_suspension_info(cmd
, user
, suspended
);
5474 else if(IsSuspended(cData
))
5476 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5477 show_suspension_info(cmd
, user
, cData
->suspended
);
5479 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5481 struct giveownership
*giveownership
;
5482 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5483 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5484 show_giveownership_info(cmd
, user
, giveownership
);
5486 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5487 reply("CSMSG_CHANNEL_END");
5489 reply("CSMSG_CHANNEL_END_CLEAN");
5493 static CHANSERV_FUNC(cmd_netinfo
)
5495 extern time_t boot_time
;
5496 extern unsigned long burst_length
;
5497 char interval
[INTERVALLEN
];
5499 reply("CSMSG_NETWORK_INFO");
5500 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5501 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5502 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5503 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5504 reply("CSMSG_NETWORK_LAMERS", banCount
);
5505 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5506 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5507 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5512 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5514 struct helpfile_table table
;
5516 struct userNode
*user
;
5521 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5522 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5523 for(nn
=0; nn
<list
->used
; nn
++)
5525 user
= list
->list
[nn
];
5526 if(user
->modes
& skip_flags
)
5530 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5533 nick
= alloca(strlen(user
->nick
)+3);
5534 sprintf(nick
, "(%s)", user
->nick
);
5538 table
.contents
[table
.length
][0] = nick
;
5541 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5544 static CHANSERV_FUNC(cmd_ircops
)
5546 reply("CSMSG_STAFF_OPERS");
5547 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5551 static CHANSERV_FUNC(cmd_helpers
)
5553 reply("CSMSG_STAFF_HELPERS");
5554 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5558 static CHANSERV_FUNC(cmd_staff
)
5560 reply("CSMSG_NETWORK_STAFF");
5561 cmd_ircops(CSFUNC_ARGS
);
5562 cmd_helpers(CSFUNC_ARGS
);
5566 static CHANSERV_FUNC(cmd_peek
)
5568 struct modeNode
*mn
;
5569 char modes
[MODELEN
];
5571 struct helpfile_table table
;
5573 irc_make_chanmode(channel
, modes
);
5575 reply("CSMSG_PEEK_INFO", channel
->name
);
5576 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5578 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5579 reply("CSMSG_PEEK_MODES", modes
);
5580 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5584 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5585 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5586 for(n
= 0; n
< channel
->members
.used
; n
++)
5588 mn
= channel
->members
.list
[n
];
5589 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5591 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5592 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5597 reply("CSMSG_PEEK_OPS");
5598 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5601 reply("CSMSG_PEEK_NO_OPS");
5602 reply("CSMSG_PEEK_END");
5606 static MODCMD_FUNC(cmd_wipeinfo
)
5608 struct handle_info
*victim
;
5609 struct userData
*ud
, *actor
, *real_actor
;
5610 unsigned int override
= 0;
5613 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5614 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5615 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5617 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5619 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5622 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5624 reply("MSG_USER_OUTRANKED", victim
->handle
);
5627 if((ud
!= real_actor
) && (!real_actor
|| (ud
->access
>= real_actor
->access
)))
5628 override
= CMD_LOG_OVERRIDE
;
5632 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5633 return 1 | override
;
5637 resync_channel(struct chanNode
*channel
)
5639 struct mod_chanmode
*changes
;
5640 struct chanData
*cData
= channel
->channel_info
;
5641 unsigned int ii
, used
;
5643 /* 6 = worst case -ovh+ovh on everyone */
5644 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5645 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5647 struct modeNode
*mn
= channel
->members
.list
[ii
];
5648 struct userData
*uData
;
5650 if(IsService(mn
->user
))
5654 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5656 /* If the channel is in no-mode mode, de-mode EVERYONE */
5657 if(cData
->chOpts
[chAutomode
] == 'n')
5661 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5662 changes
->args
[used
++].u
.member
= mn
;
5665 else /* Give various userlevels their modes.. */
5667 if(uData
&& uData
->access
>= UL_OP
)
5669 if(!(mn
->modes
& MODE_CHANOP
))
5671 changes
->args
[used
].mode
= MODE_CHANOP
;
5672 changes
->args
[used
++].u
.member
= mn
;
5675 else if(uData
&& uData
->access
>= UL_HALFOP
)
5677 if(mn
->modes
& MODE_CHANOP
)
5679 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5680 changes
->args
[used
++].u
.member
= mn
;
5682 if(!(mn
->modes
& MODE_HALFOP
))
5684 changes
->args
[used
].mode
= MODE_HALFOP
;
5685 changes
->args
[used
++].u
.member
= mn
;
5688 else if(uData
&& uData
->access
>= UL_PEON
)
5690 if(mn
->modes
& MODE_CHANOP
)
5692 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5693 changes
->args
[used
++].u
.member
= mn
;
5695 if(mn
->modes
& MODE_HALFOP
)
5697 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5698 changes
->args
[used
++].u
.member
= mn
;
5700 /* Don't voice peons if were in mode m */
5701 if( cData
->chOpts
[chAutomode
] == 'm')
5703 if(mn
->modes
& MODE_VOICE
)
5705 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5706 changes
->args
[used
++].u
.member
= mn
;
5709 /* otherwise, make user they do have voice */
5710 else if(!(mn
->modes
& MODE_VOICE
))
5712 changes
->args
[used
].mode
= MODE_VOICE
;
5713 changes
->args
[used
++].u
.member
= mn
;
5716 else /* They arnt on the userlist.. */
5718 /* If we voice everyone, but they dont.. */
5719 if(cData
->chOpts
[chAutomode
] == 'v')
5721 /* Remove anything except v */
5722 if(mn
->modes
& ~MODE_VOICE
)
5724 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5725 changes
->args
[used
++].u
.member
= mn
;
5728 if(!(mn
->modes
& MODE_VOICE
))
5730 changes
->args
[used
].mode
= MODE_VOICE
;
5731 changes
->args
[used
++].u
.member
= mn
;
5734 /* If we hop everyone, but they dont.. */
5735 else if(cData
->chOpts
[chAutomode
] == 'h')
5737 /* Remove anything except h */
5738 if(mn
->modes
& ~MODE_HALFOP
)
5740 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5741 changes
->args
[used
++].u
.member
= mn
;
5744 if(!(mn
->modes
& MODE_HALFOP
))
5746 changes
->args
[used
].mode
= MODE_HALFOP
;
5747 changes
->args
[used
++].u
.member
= mn
;
5750 /* If we op everyone, but they dont.. */
5751 else if(cData
->chOpts
[chAutomode
] == 'o')
5753 /* Remove anything except h */
5754 if(mn
->modes
& ~MODE_CHANOP
)
5756 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5757 changes
->args
[used
++].u
.member
= mn
;
5760 if(!(mn
->modes
& MODE_CHANOP
))
5762 changes
->args
[used
].mode
= MODE_CHANOP
;
5763 changes
->args
[used
++].u
.member
= mn
;
5766 /* they have no excuse for having modes, de-everything them */
5771 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5772 changes
->args
[used
++].u
.member
= mn
;
5778 changes
->argc
= used
;
5779 mod_chanmode_announce(chanserv
, channel
, changes
);
5780 mod_chanmode_free(changes
);
5783 static CHANSERV_FUNC(cmd_resync
)
5785 resync_channel(channel
);
5786 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5790 static CHANSERV_FUNC(cmd_seen
)
5792 struct userData
*uData
;
5793 struct handle_info
*handle
;
5794 char seen
[INTERVALLEN
];
5798 if(!irccasecmp(argv
[1], chanserv
->nick
))
5800 reply("CSMSG_IS_CHANSERV");
5804 if(!(handle
= get_handle_info(argv
[1])))
5806 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5810 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5812 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5817 reply("CSMSG_USER_PRESENT", handle
->handle
);
5818 else if(uData
->seen
)
5819 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5821 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5823 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5824 reply("CSMSG_USER_VACATION", handle
->handle
);
5829 static MODCMD_FUNC(cmd_names
)
5831 struct userNode
*targ
;
5832 struct userData
*targData
;
5833 unsigned int ii
, pos
;
5836 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5838 targ
= channel
->members
.list
[ii
]->user
;
5839 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5842 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5845 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5849 if(IsUserSuspended(targData
))
5851 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5854 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5855 reply("CSMSG_END_NAMES", channel
->name
);
5860 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5862 switch(ntype
->visible_type
)
5864 case NOTE_VIS_ALL
: return 1;
5865 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5866 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5871 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5873 struct userData
*uData
;
5875 switch(ntype
->set_access_type
)
5877 case NOTE_SET_CHANNEL_ACCESS
:
5878 if(!user
->handle_info
)
5880 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5882 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5883 case NOTE_SET_CHANNEL_SETTER
:
5884 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5885 case NOTE_SET_PRIVILEGED
: default:
5886 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5890 static CHANSERV_FUNC(cmd_note
)
5892 struct chanData
*cData
;
5894 struct note_type
*ntype
;
5896 cData
= channel
->channel_info
;
5899 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5903 /* If no arguments, show all visible notes for the channel. */
5909 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5911 note
= iter_data(it
);
5912 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5915 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5916 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5919 reply("CSMSG_NOTELIST_END", channel
->name
);
5921 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5923 /* If one argument, show the named note. */
5926 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5927 && note_type_visible_to_user(cData
, note
->type
, user
))
5929 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5931 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5932 && note_type_visible_to_user(NULL
, ntype
, user
))
5934 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5939 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5943 /* Assume they're trying to set a note. */
5947 ntype
= dict_find(note_types
, argv
[1], NULL
);
5950 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5953 else if(note_type_settable_by_user(channel
, ntype
, user
))
5955 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5956 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5957 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5958 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5959 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5961 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5963 /* The note is viewable to staff only, so return 0
5964 to keep the invocation from getting logged (or
5965 regular users can see it in !events). */
5971 reply("CSMSG_NO_ACCESS");
5978 static CHANSERV_FUNC(cmd_delnote
)
5983 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5984 || !note_type_settable_by_user(channel
, note
->type
, user
))
5986 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5989 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5990 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5994 static CHANSERV_FUNC(cmd_last
)
6000 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
6002 if(numoflines
< 1 || numoflines
> 200)
6004 reply("CSMSG_LAST_INVALID");
6007 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
6011 static CHANSERV_FUNC(cmd_events
)
6013 struct logSearch discrim
;
6014 struct logReport report
;
6015 unsigned int matches
, limit
;
6017 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
6018 if(limit
< 1 || limit
> 200)
6021 memset(&discrim
, 0, sizeof(discrim
));
6022 discrim
.masks
.bot
= chanserv
;
6023 discrim
.masks
.channel_name
= channel
->name
;
6025 discrim
.masks
.command
= argv
[2];
6026 discrim
.limit
= limit
;
6027 discrim
.max_time
= INT_MAX
;
6028 discrim
.severities
= 1 << LOG_COMMAND
;
6029 report
.reporter
= chanserv
;
6031 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
6032 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6034 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
6036 reply("MSG_MATCH_COUNT", matches
);
6038 reply("MSG_NO_MATCHES");
6042 static CHANSERV_FUNC(cmd_say
)
6048 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6049 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
6051 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6053 struct handle_info
*hi
;
6054 struct userNode
*authed
;
6057 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6059 if (!(hi
= get_handle_info(argv
[1] + 1)))
6061 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6065 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6066 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "%s", msg
);
6068 else if(GetUserH(argv
[1]))
6071 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6072 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
6076 reply("MSG_NOT_TARGET_NAME");
6082 static CHANSERV_FUNC(cmd_emote
)
6088 /* CTCP is so annoying. */
6089 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6090 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6092 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6094 struct handle_info
*hi
;
6095 struct userNode
*authed
;
6098 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6100 if (!(hi
= get_handle_info(argv
[1] + 1)))
6102 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6106 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6107 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6109 else if(GetUserH(argv
[1]))
6111 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6112 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6116 reply("MSG_NOT_TARGET_NAME");
6122 struct channelList
*
6123 chanserv_support_channels(void)
6125 return &chanserv_conf
.support_channels
;
6128 static CHANSERV_FUNC(cmd_expire
)
6130 int channel_count
= registered_channels
;
6131 expire_channels(NULL
);
6132 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
6137 chanserv_expire_suspension(void *data
)
6139 struct suspended
*suspended
= data
;
6140 struct chanNode
*channel
;
6143 /* Update the channel registration data structure. */
6144 if(!suspended
->expires
|| (now
< suspended
->expires
))
6145 suspended
->revoked
= now
;
6146 channel
= suspended
->cData
->channel
;
6147 suspended
->cData
->channel
= channel
;
6148 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
6150 /* If appropriate, re-join ChanServ to the channel. */
6151 if(!IsOffChannel(suspended
->cData
))
6153 spamserv_cs_suspend(channel
, 0, 0, NULL
);
6154 ss_cs_join_channel(channel
, 1);
6157 /* Mark everyone currently in the channel as present. */
6158 for(ii
= 0; ii
< channel
->members
.used
; ++ii
)
6160 struct userData
*uData
= GetChannelAccess(suspended
->cData
, channel
->members
.list
[ii
]->user
->handle_info
);
6169 static CHANSERV_FUNC(cmd_csuspend
)
6171 struct suspended
*suspended
;
6172 char reason
[MAXLEN
];
6173 time_t expiry
, duration
;
6174 struct userData
*uData
;
6178 if(IsProtected(channel
->channel_info
))
6180 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
6184 if(argv
[1][0] == '!')
6186 else if(IsSuspended(channel
->channel_info
))
6188 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
6189 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
6193 if(!strcmp(argv
[1], "0"))
6195 else if((duration
= ParseInterval(argv
[1])))
6196 expiry
= now
+ duration
;
6199 reply("MSG_INVALID_DURATION", argv
[1]);
6203 unsplit_string(argv
+ 2, argc
- 2, reason
);
6205 suspended
= calloc(1, sizeof(*suspended
));
6206 suspended
->revoked
= 0;
6207 suspended
->issued
= now
;
6208 suspended
->suspender
= strdup(user
->handle_info
->handle
);
6209 suspended
->expires
= expiry
;
6210 suspended
->reason
= strdup(reason
);
6211 suspended
->cData
= channel
->channel_info
;
6212 suspended
->previous
= suspended
->cData
->suspended
;
6213 suspended
->cData
->suspended
= suspended
;
6215 if(suspended
->expires
)
6216 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6218 if(IsSuspended(channel
->channel_info
))
6220 suspended
->previous
->revoked
= now
;
6221 if(suspended
->previous
->expires
)
6222 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
6224 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
6225 channel
->name
, suspended
->suspender
);
6229 /* Mark all users in channel as absent. */
6230 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
6239 /* Mark the channel as suspended, then part. */
6240 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
6241 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
6242 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
6243 reply("CSMSG_SUSPENDED", channel
->name
);
6244 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
6245 channel
->name
, suspended
->suspender
);
6250 static CHANSERV_FUNC(cmd_cunsuspend
)
6252 struct suspended
*suspended
;
6254 if(!IsSuspended(channel
->channel_info
))
6256 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
6260 suspended
= channel
->channel_info
->suspended
;
6262 /* Expire the suspension and join ChanServ to the channel. */
6263 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
6264 chanserv_expire_suspension(suspended
);
6265 reply("CSMSG_UNSUSPENDED", channel
->name
);
6266 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
6267 channel
->name
, user
->handle_info
->handle
);
6271 typedef struct chanservSearch
6279 unsigned long flags
;
6283 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
6286 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
6291 search
= malloc(sizeof(struct chanservSearch
));
6292 memset(search
, 0, sizeof(*search
));
6295 for(i
= 0; i
< argc
; i
++)
6297 /* Assume all criteria require arguments. */
6300 reply("MSG_MISSING_PARAMS", argv
[i
]);
6304 if(!irccasecmp(argv
[i
], "name"))
6305 search
->name
= argv
[++i
];
6306 else if(!irccasecmp(argv
[i
], "registrar"))
6307 search
->registrar
= argv
[++i
];
6308 else if(!irccasecmp(argv
[i
], "unvisited"))
6309 search
->unvisited
= ParseInterval(argv
[++i
]);
6310 else if(!irccasecmp(argv
[i
], "registered"))
6311 search
->registered
= ParseInterval(argv
[++i
]);
6312 else if(!irccasecmp(argv
[i
], "flags"))
6315 if(!irccasecmp(argv
[i
], "nodelete"))
6316 search
->flags
|= CHANNEL_NODELETE
;
6317 else if(!irccasecmp(argv
[i
], "suspended"))
6318 search
->flags
|= CHANNEL_SUSPENDED
;
6319 else if(!irccasecmp(argv
[i
], "unreviewed"))
6320 search
->flags
|= CHANNEL_UNREVIEWED
;
6323 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
6327 else if(!irccasecmp(argv
[i
], "limit"))
6328 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
6331 reply("MSG_INVALID_CRITERIA", argv
[i
]);
6336 if(search
->name
&& !strcmp(search
->name
, "*"))
6338 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
6339 search
->registrar
= 0;
6348 chanserv_channel_match(struct chanData
*channel
, search_t search
)
6350 const char *name
= channel
->channel
->name
;
6351 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
6352 (search
->registrar
&& !channel
->registrar
) ||
6353 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
6354 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
6355 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
6356 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
6363 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
6365 struct chanData
*channel
;
6366 unsigned int matches
= 0;
6368 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
6370 if(!chanserv_channel_match(channel
, search
))
6380 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
6385 search_print(struct chanData
*channel
, void *data
)
6387 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
6390 static CHANSERV_FUNC(cmd_search
)
6393 unsigned int matches
;
6394 channel_search_func action
;
6398 if(!irccasecmp(argv
[1], "count"))
6399 action
= search_count
;
6400 else if(!irccasecmp(argv
[1], "print"))
6401 action
= search_print
;
6404 reply("CSMSG_ACTION_INVALID", argv
[1]);
6408 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
6412 if(action
== search_count
)
6413 search
->limit
= INT_MAX
;
6415 if(action
== search_print
)
6417 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6418 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6422 matches
= chanserv_channel_search(search
, action
, user
);
6425 reply("MSG_MATCH_COUNT", matches
);
6427 reply("MSG_NO_MATCHES");
6433 static CHANSERV_FUNC(cmd_unvisited
)
6435 struct chanData
*cData
;
6436 time_t interval
= chanserv_conf
.channel_expire_delay
;
6437 char buffer
[INTERVALLEN
];
6438 unsigned int limit
= 25, matches
= 0;
6442 interval
= ParseInterval(argv
[1]);
6444 limit
= atoi(argv
[2]);
6447 intervalString(buffer
, interval
, user
->handle_info
);
6448 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
6450 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
6452 if((now
- cData
->visited
) < interval
)
6455 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
6456 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
6463 static MODCMD_FUNC(chan_opt_unreviewed
)
6465 struct chanData
*cData
= channel
->channel_info
;
6466 int value
= (cData
->flags
& CHANNEL_UNREVIEWED
) ? 1 : 0;
6472 /* The two directions can have different ACLs. */
6473 if(enabled_string(argv
[1]))
6475 else if(disabled_string(argv
[1]))
6479 reply("MSG_INVALID_BINARY", argv
[1]);
6483 if (new_value
!= value
)
6485 struct svccmd
*subcmd
;
6486 char subcmd_name
[32];
6488 snprintf(subcmd_name
, sizeof(subcmd_name
), "%s %s", argv
[0], (new_value
? "on" : "off"));
6489 subcmd
= dict_find(cmd
->parent
->commands
, subcmd_name
, NULL
);
6492 reply("MSG_COMMAND_DISABLED", subcmd_name
);
6495 else if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
6499 cData
->flags
|= CHANNEL_UNREVIEWED
;
6502 free(cData
->registrar
);
6503 cData
->registrar
= strdup(user
->handle_info
->handle
);
6504 cData
->flags
&= ~CHANNEL_UNREVIEWED
;
6511 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_ON"));
6513 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_OFF"));
6517 static MODCMD_FUNC(chan_opt_defaulttopic
)
6523 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6525 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6529 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6531 free(channel
->channel_info
->topic
);
6532 if(topic
[0] == '*' && topic
[1] == 0)
6534 topic
= channel
->channel_info
->topic
= NULL
;
6538 topic
= channel
->channel_info
->topic
= strdup(topic
);
6539 if(channel
->channel_info
->topic_mask
6540 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6541 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6543 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6546 if(channel
->channel_info
->topic
)
6547 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6549 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6553 static MODCMD_FUNC(chan_opt_topicmask
)
6557 struct chanData
*cData
= channel
->channel_info
;
6560 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6562 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6566 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6568 if(cData
->topic_mask
)
6569 free(cData
->topic_mask
);
6570 if(mask
[0] == '*' && mask
[1] == 0)
6572 cData
->topic_mask
= 0;
6576 cData
->topic_mask
= strdup(mask
);
6578 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6579 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6580 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6584 if(channel
->channel_info
->topic_mask
)
6585 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6587 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6591 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6595 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6599 if(greeting
[0] == '*' && greeting
[1] == 0)
6603 unsigned int length
= strlen(greeting
);
6604 if(length
> chanserv_conf
.greeting_length
)
6606 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6609 *data
= strdup(greeting
);
6618 reply(name
, user_find_message(user
, "MSG_NONE"));
6622 static MODCMD_FUNC(chan_opt_greeting
)
6624 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6627 static MODCMD_FUNC(chan_opt_usergreeting
)
6629 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6632 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6634 unsigned int charmax
;
6637 charmax
= atoi(argv
[1]);
6638 if ((charmax
> 0) && (charmax
< chanserv_conf
.max_userinfo_length
))
6639 channel
->channel_info
->maxsetinfo
= charmax
;
6642 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6646 static MODCMD_FUNC(chan_opt_modes
)
6648 struct mod_chanmode
*new_modes
;
6649 char modes
[MODELEN
];
6653 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6654 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6658 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6660 reply("CSMSG_NO_ACCESS");
6663 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6665 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6667 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6669 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6672 else if(new_modes
->argc
> 1)
6674 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6675 mod_chanmode_free(new_modes
);
6680 channel
->channel_info
->modes
= *new_modes
;
6681 modcmd_chanmode_announce(new_modes
);
6682 mod_chanmode_free(new_modes
);
6686 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6688 reply("CSMSG_SET_MODES", modes
);
6690 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6694 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6696 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6698 struct chanData
*cData
= channel
->channel_info
;
6703 /* Set flag according to value. */
6704 if(enabled_string(argv
[1]))
6706 cData
->flags
|= mask
;
6709 else if(disabled_string(argv
[1]))
6711 cData
->flags
&= ~mask
;
6716 reply("MSG_INVALID_BINARY", argv
[1]);
6722 /* Find current option value. */
6723 value
= (cData
->flags
& mask
) ? 1 : 0;
6727 reply(name
, user_find_message(user
, "MSG_ON"));
6729 reply(name
, user_find_message(user
, "MSG_OFF"));
6733 static MODCMD_FUNC(chan_opt_nodelete
)
6735 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6737 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6741 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6744 static MODCMD_FUNC(chan_opt_dynlimit
)
6746 struct mod_chanmode change
;
6749 if (disabled_string(argv
[1])) {
6750 mod_chanmode_init(&change
);
6751 change
.modes_clear
|= MODE_LIMIT
;
6752 mod_chanmode_announce(chanserv
, channel
, &change
);
6756 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6759 static MODCMD_FUNC(chan_opt_offchannel
)
6761 struct chanData
*cData
= channel
->channel_info
;
6766 /* Set flag according to value. */
6767 if(enabled_string(argv
[1]))
6769 if(!IsOffChannel(cData
))
6770 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6771 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6774 else if(disabled_string(argv
[1]))
6776 if(IsOffChannel(cData
))
6778 struct mod_chanmode change
;
6779 mod_chanmode_init(&change
);
6781 change
.args
[0].mode
= MODE_CHANOP
;
6782 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6783 mod_chanmode_announce(chanserv
, channel
, &change
);
6785 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6790 reply("MSG_INVALID_BINARY", argv
[1]);
6796 /* Find current option value. */
6797 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6801 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6803 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6807 static MODCMD_FUNC(chan_opt_defaults
)
6809 struct userData
*uData
;
6810 struct chanData
*cData
;
6811 const char *confirm
;
6812 enum levelOption lvlOpt
;
6813 enum charOption chOpt
;
6815 cData
= channel
->channel_info
;
6816 uData
= GetChannelUser(cData
, user
->handle_info
);
6817 if(!uData
|| (uData
->access
< UL_OWNER
))
6819 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6822 confirm
= make_confirmation_string(uData
);
6823 if((argc
< 2) || strcmp(argv
[1], confirm
))
6825 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6828 cData
->flags
= (CHANNEL_DEFAULT_FLAGS
& ~CHANNEL_PRESERVED_FLAGS
)
6829 | (cData
->flags
& CHANNEL_PRESERVED_FLAGS
);
6830 cData
->modes
= chanserv_conf
.default_modes
;
6831 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6832 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6833 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6834 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6835 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6840 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6842 struct chanData
*cData
= channel
->channel_info
;
6843 struct userData
*uData
;
6844 unsigned short value
;
6848 if(!check_user_level(channel
, user
, option
, 1, 1))
6850 reply("CSMSG_CANNOT_SET");
6853 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6854 if(!value
&& strcmp(argv
[1], "0"))
6856 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6859 uData
= GetChannelUser(cData
, user
->handle_info
);
6860 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6862 reply("CSMSG_BAD_SETLEVEL");
6868 /* This test only applies to owners, since non-owners
6869 * trying to set an option to above their level get caught
6870 * by the CSMSG_BAD_SETLEVEL test above.
6872 if(value
> uData
->access
)
6874 reply("CSMSG_BAD_SETTERS");
6881 cData
->lvlOpts
[option
] = value
;
6883 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6887 static MODCMD_FUNC(chan_opt_enfops
)
6889 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6892 static MODCMD_FUNC(chan_opt_enfhalfops
)
6894 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6896 static MODCMD_FUNC(chan_opt_enfmodes
)
6898 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6901 static MODCMD_FUNC(chan_opt_enftopic
)
6903 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6906 static MODCMD_FUNC(chan_opt_pubcmd
)
6908 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6911 static MODCMD_FUNC(chan_opt_setters
)
6913 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6916 static MODCMD_FUNC(chan_opt_userinfo
)
6918 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6921 static MODCMD_FUNC(chan_opt_topicsnarf
)
6923 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6926 static MODCMD_FUNC(chan_opt_inviteme
)
6928 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6931 /* TODO: Make look like this when no args are
6933 * -X3- -------------------------------
6934 * -X3- BanTimeout: Bans are removed:
6935 * -X3- ----- * indicates current -----
6936 * -X3- 0: [*] Never.
6937 * -X3- 1: [ ] After 10 minutes.
6938 * -X3- 2: [ ] After 2 hours.
6939 * -X3- 3: [ ] After 4 hours.
6940 * -X3- 4: [ ] After 24 hours.
6941 * -X3- 5: [ ] After one week.
6942 * -X3- ------------- End -------------
6945 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6947 struct chanData
*cData
= channel
->channel_info
;
6948 int count
= charOptions
[option
].count
, idx
;
6952 idx
= atoi(argv
[1]);
6954 if(!isdigit(argv
[1][0]) || (idx
< 0) || (idx
>= count
))
6956 reply("CSMSG_INVALID_NUMERIC", idx
);
6957 /* Show possible values. */
6958 for(idx
= 0; idx
< count
; idx
++)
6959 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
6963 cData
->chOpts
[option
] = charOptions
[option
].values
[idx
].value
;
6967 /* Find current option value. */
6970 (idx
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[idx
].value
);
6974 /* Somehow, the option value is corrupt; reset it to the default. */
6975 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6980 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
6984 static MODCMD_FUNC(chan_opt_automode
)
6986 if(check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
6988 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
6990 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6993 static MODCMD_FUNC(chan_opt_protect
)
6995 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6998 static MODCMD_FUNC(chan_opt_toys
)
7000 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
7003 static MODCMD_FUNC(chan_opt_ctcpreaction
)
7005 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
7008 static MODCMD_FUNC(chan_opt_bantimeout
)
7010 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
7013 static MODCMD_FUNC(chan_opt_topicrefresh
)
7015 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
7018 static MODCMD_FUNC(chan_opt_resync
)
7020 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
7023 static struct svccmd_list set_shows_list
;
7026 handle_svccmd_unbind(struct svccmd
*target
) {
7028 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
7029 if(target
== set_shows_list
.list
[ii
])
7030 set_shows_list
.used
= 0;
7033 static CHANSERV_FUNC(cmd_set
)
7035 struct svccmd
*subcmd
;
7039 /* Check if we need to (re-)initialize set_shows_list. */
7040 if(!set_shows_list
.used
)
7042 if(!set_shows_list
.size
)
7044 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
7045 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
7047 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
7049 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
7050 sprintf(buf
, "%s %s", argv
[0], name
);
7051 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7054 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
7057 svccmd_list_append(&set_shows_list
, subcmd
);
7063 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
7064 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
7066 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
7068 subcmd
= set_shows_list
.list
[ii
];
7069 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
7071 reply("CSMSG_CHANNEL_OPTIONS_END");
7075 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7076 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7079 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7082 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
7084 reply("CSMSG_NO_ACCESS");
7090 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7094 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
7096 struct userData
*uData
;
7098 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7101 reply("CSMSG_NOT_USER", channel
->name
);
7107 /* Just show current option value. */
7109 else if(enabled_string(argv
[1]))
7111 uData
->flags
|= mask
;
7113 else if(disabled_string(argv
[1]))
7115 uData
->flags
&= ~mask
;
7119 reply("MSG_INVALID_BINARY", argv
[1]);
7123 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
7127 static MODCMD_FUNC(user_opt_autoop
)
7129 struct userData
*uData
;
7131 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7134 reply("CSMSG_NOT_USER", channel
->name
);
7137 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
7138 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
7140 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
7143 static MODCMD_FUNC(user_opt_autoinvite
)
7145 if((argc
> 1) && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
7147 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
7149 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
7152 static MODCMD_FUNC(user_opt_autojoin
)
7154 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
7157 static MODCMD_FUNC(user_opt_info
)
7159 struct userData
*uData
;
7162 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7166 /* If they got past the command restrictions (which require access)
7167 * but fail this test, we have some fool with security override on.
7169 reply("CSMSG_NOT_USER", channel
->name
);
7176 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
7177 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
7179 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
7182 bp
= strcspn(infoline
, "\001");
7185 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
7190 if(infoline
[0] == '*' && infoline
[1] == 0)
7193 uData
->info
= strdup(infoline
);
7196 reply("CSMSG_USET_INFO", uData
->info
);
7198 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
7202 struct svccmd_list uset_shows_list
;
7204 static CHANSERV_FUNC(cmd_uset
)
7206 struct svccmd
*subcmd
;
7210 /* Check if we need to (re-)initialize uset_shows_list. */
7211 if(!uset_shows_list
.used
)
7215 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7218 if(!uset_shows_list
.size
)
7220 uset_shows_list
.size
= ArrayLength(options
);
7221 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
7223 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
7225 const char *name
= options
[ii
];
7226 sprintf(buf
, "%s %s", argv
[0], name
);
7227 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7230 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
7233 svccmd_list_append(&uset_shows_list
, subcmd
);
7239 /* Do this so options are presented in a consistent order. */
7240 reply("CSMSG_USER_OPTIONS");
7241 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
7242 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
7246 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7247 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7250 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7254 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7257 static CHANSERV_FUNC(cmd_giveownership
)
7259 struct handle_info
*new_owner_hi
;
7260 struct userData
*new_owner
;
7261 struct userData
*curr_user
;
7262 struct userData
*invoker
;
7263 struct chanData
*cData
= channel
->channel_info
;
7264 struct do_not_register
*dnr
;
7265 struct giveownership
*giveownership
;
7266 const char *confirm
;
7267 unsigned int force
, override
;
7268 unsigned short co_access
, new_owner_old_access
;
7269 char transfer_reason
[MAXLEN
];
7272 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
7273 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
7275 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
7276 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
7277 && (uData
->access
> 500)
7278 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
7279 || uData
->access
< 500));
7282 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
7284 struct userData
*owner
= NULL
;
7285 for(curr_user
= channel
->channel_info
->users
;
7287 curr_user
= curr_user
->next
)
7289 if(curr_user
->access
!= UL_OWNER
)
7293 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
7300 else if(!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
7302 char delay
[INTERVALLEN
];
7303 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
7304 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
7308 reply("CSMSG_NO_OWNER", channel
->name
);
7311 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
7313 if(new_owner_hi
== user
->handle_info
)
7315 reply("CSMSG_NO_TRANSFER_SELF");
7318 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
7323 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_OWNER
- 1, 0, NULL
, 0);
7327 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
7331 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
7333 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
7336 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
7337 if(!IsHelping(user
))
7338 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
7340 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
7344 invoker
= GetChannelUser(cData
, user
->handle_info
);
7345 if(invoker
->access
<= UL_OWNER
)
7347 confirm
= make_confirmation_string(curr_user
);
7348 if((argc
< 3) || strcmp(argv
[2], confirm
))
7350 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi
->handle
, confirm
);
7355 new_owner_old_access
= new_owner
->access
;
7356 if(new_owner
->access
>= UL_COOWNER
)
7357 co_access
= new_owner
->access
;
7359 co_access
= UL_COOWNER
;
7360 new_owner
->access
= UL_OWNER
;
7362 curr_user
->access
= co_access
;
7363 cData
->ownerTransfer
= now
;
7365 giveownership
= calloc(1, sizeof(*giveownership
));
7366 giveownership
->issued
= now
;
7367 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
7368 giveownership
->target
= strdup(new_owner_hi
->handle
);
7369 giveownership
->target_access
= new_owner_old_access
;
7372 if(argc
> (2 + force
))
7374 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
7375 giveownership
->reason
= strdup(transfer_reason
);
7377 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
7380 giveownership
->previous
= channel
->channel_info
->giveownership
;
7381 channel
->channel_info
->giveownership
= giveownership
;
7383 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
7384 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
7385 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
7390 chanserv_expire_user_suspension(void *data
)
7392 struct userData
*target
= data
;
7394 target
->expires
= 0;
7395 target
->flags
&= ~USER_SUSPENDED
;
7398 static CHANSERV_FUNC(cmd_suspend
)
7400 struct handle_info
*hi
;
7401 struct userData
*actor
, *real_actor
, *target
;
7402 unsigned int override
= 0;
7406 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7407 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7408 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7409 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7411 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7414 if(target
->access
>= actor
->access
)
7416 reply("MSG_USER_OUTRANKED", hi
->handle
);
7419 if(target
->flags
& USER_SUSPENDED
)
7421 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
7426 target
->present
= 0;
7429 if(!strcmp(argv
[2], "0"))
7433 unsigned int duration
;
7434 if(!(duration
= ParseInterval(argv
[2])))
7436 reply("MSG_INVALID_DURATION", argv
[2]);
7439 expiry
= now
+ duration
;
7442 target
->expires
= expiry
;
7445 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
7447 if(!real_actor
|| target
->access
>= real_actor
->access
)
7448 override
= CMD_LOG_OVERRIDE
;
7449 target
->flags
|= USER_SUSPENDED
;
7450 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
7451 return 1 | override
;
7454 static CHANSERV_FUNC(cmd_unsuspend
)
7456 struct handle_info
*hi
;
7457 struct userData
*actor
= NULL
, *real_actor
= NULL
, *target
;
7458 unsigned int override
= 0;
7461 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7462 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7463 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7465 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7468 if(target
->access
>= actor
->access
)
7470 reply("MSG_USER_OUTRANKED", hi
->handle
);
7473 if(!(target
->flags
& USER_SUSPENDED
))
7475 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
7478 if(!real_actor
|| target
->access
>= real_actor
->access
)
7479 override
= CMD_LOG_OVERRIDE
;
7480 target
->flags
&= ~USER_SUSPENDED
;
7481 scan_user_presence(target
, NULL
);
7482 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
7483 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
7484 return 1 | override
;
7487 static MODCMD_FUNC(cmd_deleteme
)
7489 struct handle_info
*hi
;
7490 struct userData
*target
;
7491 const char *confirm_string
;
7492 unsigned short access
;
7495 hi
= user
->handle_info
;
7496 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7498 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7501 if(target
->access
== UL_OWNER
)
7503 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
7506 confirm_string
= make_confirmation_string(target
);
7507 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
7509 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
7512 access
= target
->access
;
7513 channel_name
= strdup(channel
->name
);
7514 del_channel_user(target
, 1);
7515 reply("CSMSG_DELETED_YOU", access
, channel_name
);
7521 chanserv_refresh_topics(UNUSED_ARG(void *data
))
7523 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7524 struct chanData
*cData
;
7527 for(cData
= channelList
; cData
; cData
= cData
->next
)
7529 if(IsSuspended(cData
))
7531 opt
= cData
->chOpts
[chTopicRefresh
];
7534 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
7537 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
7538 cData
->last_refresh
= refresh_num
;
7540 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
7544 chanserv_auto_resync(UNUSED_ARG(void *data
))
7546 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7547 struct chanData
*cData
;
7550 for(cData
= channelList
; cData
; cData
= cData
->next
)
7552 if(IsSuspended(cData
)) continue;
7553 opt
= cData
->chOpts
[chResync
];
7554 if(opt
== 'n') continue;
7555 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
7556 resync_channel(cData
->channel
);
7557 cData
->last_resync
= refresh_num
;
7559 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7562 static CHANSERV_FUNC(cmd_unf
)
7566 char response
[MAXLEN
];
7567 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7568 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7569 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7572 reply("CSMSG_UNF_RESPONSE");
7576 static CHANSERV_FUNC(cmd_ping
)
7580 char response
[MAXLEN
];
7581 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7582 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7583 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7586 reply("CSMSG_PING_RESPONSE");
7590 static CHANSERV_FUNC(cmd_wut
)
7594 char response
[MAXLEN
];
7595 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7596 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7597 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7600 reply("CSMSG_WUT_RESPONSE");
7604 static CHANSERV_FUNC(cmd_roulette
)
7607 struct chanData
*cData
= channel
->channel_info
;
7610 if (cData
->roulette_chamber
) {
7611 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7615 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7616 cData
->roulette_chamber
= 1 + rand() % 6;
7622 static CHANSERV_FUNC(cmd_shoot
)
7625 struct chanData
*cData
= channel
->channel_info
;
7627 if (cData
->roulette_chamber
<= 0) {
7628 struct service
*service
;
7629 if ((service
= service_find(chanserv
->nick
))) {
7630 reply("CSMSG_ROULETTE_NEW", service
->trigger
);
7635 cData
->roulette_chamber
--;
7637 if (cData
->roulette_chamber
== 0) {
7638 reply("CSMSG_ROULETTE_BANG");
7639 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7640 DelUser(user
, chanserv
, 1, "BANG!!!!");
7642 reply("CSMSG_ROULETTE_CLICK");
7649 chanserv_remove_abuse(void *data
)
7651 char *remnick
= data
;
7652 struct userNode
*user
;
7653 /* sometimes the clone was killed and maybe even the user took their nick back
7654 * (ie, an oper) so dont kill them here after all unless they are local. */
7655 if( (user
= GetUserH(remnick
)) )
7657 DelUser(user
, NULL
, 1, "");
7660 int lamepart(struct userNode
*nick
) {
7661 struct modeNode
*mn
;
7662 unsigned int count
, n
;
7664 for (n
=count
=0; n
<nick
->channels
.used
; n
++) {
7665 mn
= nick
->channels
.list
[n
];
7666 irc_svspart(chanserv
, nick
, mn
->channel
);
7672 static CHANSERV_FUNC(cmd_spin
)
7677 int type
= 0, lamep
= 1;
7680 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7688 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7689 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7690 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7692 if(chanserv_conf
.wheel
->used
< 1) {
7693 /* wheel actions not defined! eek */
7697 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7698 if(!wheel
&& *wheel
)
7701 /* enable this to be able to manually specify a result for testing:
7702 log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
7708 /* connection reset by peer */
7709 if (!strcasecmp(wheel
, "peer")) {
7710 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7712 irc_kill(chanserv
, user
, "Connection reset by peer");
7714 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7716 /* part all channels */
7717 else if (!strcasecmp(wheel
, "partall")) {
7718 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7722 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7724 /* random time gline */
7725 else if (!strcasecmp(wheel
, "gline")) {
7726 char target
[IRC_NTOP_MAX_SIZE
+ 3];
7727 int wtime
= 120 + rand() % 600;
7729 strcpy(target
, "*@");
7730 strcat(target
, user
->hostname
);
7731 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7733 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, now
, 1, 0);
7734 // irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7737 else if (!strcasecmp(wheel
, "shun")) {
7738 char target
[IRC_NTOP_MAX_SIZE
+ 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_SHUN");
7745 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, now
, 1);
7747 /* absolutely nothing */
7748 else if (!strcasecmp(wheel
, "nothing")) {
7749 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7751 /* join random chans and part em several times */
7752 else if (!strcasecmp(wheel
, "randjoin")) {
7758 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7759 while(complete
!= 1) {
7760 if (rndchans
!= 15) {
7761 chango
= 120 + rand() % 600;
7762 sputsock("%s SJ %s #%d "FMT_TIME_T
, self
->numeric
, user
->numeric
, chango
, now
);
7765 if (roundz0r
!= 1) {
7769 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7776 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7782 /* abuse line added to /whois */
7783 else if (!strcasecmp(wheel
, "abusewhois")) {
7784 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7785 irc_swhois(chanserv
, user
, "is being defecated on by services");
7787 /* kick from each channel your in */
7788 else if (!strcasecmp(wheel
, "kickall")) {
7789 unsigned int count
, n
;
7790 struct modeNode
*mn
;
7792 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7794 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7795 mn
= user
->channels
.list
[n
];
7796 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7799 /* random nick change */
7800 else if (!strcasecmp(wheel
, "nickchange")) {
7801 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7803 char *oldnick
= NULL
;
7804 char *oldident
= NULL
;
7805 char *oldhost
= NULL
;
7806 char abusednick
[NICKLEN
] = "";
7807 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7808 struct userNode
*clone
;
7810 oldnick
= strdup(user
->nick
);
7811 oldident
= strdup(user
->ident
);
7812 oldhost
= strdup(user
->hostname
);
7814 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7816 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7817 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7818 if(!GetUserH(abusednick
))
7822 SVSNickChange(user
, abusednick
);
7823 irc_svsnick(chanserv
, user
, abusednick
);
7824 clone
= AddLocalUser(oldnick
, oldident
, oldhost
, "I got abused by the wheel of misfortune :D", "+i");
7825 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7828 else if (!strcasecmp(wheel
, "kill")) {
7829 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7831 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7832 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7834 /* service ignore */
7835 else if (!strcasecmp(wheel
, "svsignore")) {
7836 int gagged
, ignoretime
= 0;
7837 char target
[IRC_NTOP_MAX_SIZE
+ 13];
7840 /* we cant gag opers, so just verbally abuse them */
7841 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7844 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7846 strcpy(target
, "*!*@");
7847 strcat(target
, user
->hostname
);
7848 ignoretime
= now
+ (1 + rand() % 120);
7850 gagged
= gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7852 /* kick and ban from each channel your in */
7853 else if (!strcasecmp(wheel
, "kickbanall")) {
7854 unsigned int count
, n
;
7855 struct modeNode
*mn
;
7856 //char ban[IRC_NTOP_MAX_SIZE + 1];
7858 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7860 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7861 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7862 struct mod_chanmode
*change
;
7863 /* struct banData *bData; */
7864 unsigned int exists
;
7865 /* int duration = 300; */
7868 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7870 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7871 mn
= user
->channels
.list
[n
];
7872 if(mn
->channel
->banlist
.used
>= MAXBANS
) {
7873 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7878 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7880 change
= mod_chanmode_alloc(1);
7881 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7882 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7885 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7886 mod_chanmode_free(change
);
7888 exists
= ChannelBanExists(mn
->channel
, ban
);
7890 change
= mod_chanmode_alloc(1);
7891 change
->args
[0].mode
= MODE_BAN
;
7892 change
->args
[0].u
.hostmask
= ban
;
7894 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7895 mod_chanmode_free(change
);
7899 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7903 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7907 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7914 static CHANSERV_FUNC(cmd_8ball
)
7916 unsigned int i
, j
, accum
;
7921 for(i
=1; i
<argc
; i
++)
7922 for(j
=0; argv
[i
][j
]; j
++)
7923 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7924 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7927 char response
[MAXLEN
];
7928 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7929 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7932 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7936 #else /* Use cool 8ball instead */
7938 void eightball(char *outcome
, int method
, unsigned int seed
)
7942 #define NUMOFCOLORS 18
7943 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7944 "white", "black", "grey", "brown",
7945 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7946 "fuchsia","turquoise","magenta", "cyan"};
7947 #define NUMOFLOCATIONS 50
7948 char balllocations
[50][55] = {
7949 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7950 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7951 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7952 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7953 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7954 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7955 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7956 "your bra", "your hair", "your bed", "the couch", "the wall",
7957 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7958 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7959 #define NUMOFPREPS 15
7960 char ballpreps
[50][50] = {
7961 "Near", "Somewhere near", "In", "In", "In",
7962 "In", "Hiding in", "Under", "Next to", "Over",
7963 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7964 #define NUMOFNUMS 34
7965 char ballnums
[50][50] = {
7966 "A hundred", "A thousand", "A few", "42",
7967 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7968 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7969 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7971 #define NUMOFMULTS 8
7972 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7975 * 0: normal (Not used in x3)
7982 if (method
== 1) /* A Color */
7986 answer
= (rand() % 12); /* Make sure this is the # of entries */
7989 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
7991 case 1: strcpy(tmp
, "Sort of a light %s color.");
7993 case 2: strcpy(tmp
, "Dark and dreary %s.");
7995 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
7997 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
7999 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
8001 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
8003 case 10: strcpy(tmp
, "Solid %s.");
8005 case 11: strcpy(tmp
, "Transparent %s.");
8007 default: strcpy(outcome
, "An invalid random number was generated.");
8010 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
8013 else if (method
== 2) /* Location */
8015 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
8017 else if (method
== 3) /* Number of ___ */
8019 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
8023 //Debug(DBGWARNING, "Error in 8ball.");
8028 static CHANSERV_FUNC(cmd_8ball
)
8030 char *word1
, *word2
, *word3
;
8031 static char eb
[MAXLEN
];
8032 unsigned int accum
, i
, j
;
8036 for(i
=1; i
<argc
; i
++)
8037 for(j
=0; argv
[i
][j
]; j
++)
8038 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
8040 accum
+= time(NULL
)/3600;
8042 word2
= argc
>2?argv
[2]:"";
8043 word3
= argc
>3?argv
[3]:"";
8046 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
8047 eightball(eb
, 1, accum
);
8048 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
8049 eightball(eb
, 1, accum
);
8050 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
8051 eightball(eb
, 1, accum
);
8052 /*** LOCATION *****/
8057 (strcasecmp(word1
, "where") == 0) &&
8058 (strcasecmp(word2
, "is") == 0)
8062 strcasecmp(word1
, "where's") == 0
8065 eightball(eb
, 2, accum
);
8067 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
8068 eightball(eb
, 3, accum
);
8072 /* Generic 8ball question.. so pull from x3.conf srvx style */
8075 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
8078 char response
[MAXLEN
];
8079 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
8080 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8083 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
8089 char response
[MAXLEN
];
8090 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
8091 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8094 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
8099 static CHANSERV_FUNC(cmd_d
)
8101 unsigned long sides
, count
, modifier
, ii
, total
;
8102 char response
[MAXLEN
], *sep
;
8106 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
8116 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
8117 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
8121 else if((sep
[0] == '-') && isdigit(sep
[1]))
8122 modifier
= strtoul(sep
, NULL
, 10);
8123 else if((sep
[0] == '+') && isdigit(sep
[1]))
8124 modifier
= strtoul(sep
+1, NULL
, 10);
8131 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
8136 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
8139 for(total
= ii
= 0; ii
< count
; ++ii
)
8140 total
+= (rand() % sides
) + 1;
8143 if((count
> 1) || modifier
)
8145 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
8146 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
8150 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
8151 sprintf(response
, fmt
, total
, sides
);
8154 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8156 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8160 static CHANSERV_FUNC(cmd_huggle
)
8162 /* CTCP must be via PRIVMSG, never notice */
8164 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
8166 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
8170 static CHANSERV_FUNC(cmd_calc
)
8172 char response
[MAXLEN
];
8175 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8178 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8180 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8184 static CHANSERV_FUNC(cmd_reply
)
8188 unsplit_string(argv
+ 1, argc
- 1, NULL
);
8191 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8193 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
8198 chanserv_adjust_limit(void *data
)
8200 struct mod_chanmode change
;
8201 struct chanData
*cData
= data
;
8202 struct chanNode
*channel
= cData
->channel
;
8205 if(IsSuspended(cData
))
8208 cData
->limitAdjusted
= now
;
8209 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
8210 if(cData
->modes
.modes_set
& MODE_LIMIT
)
8212 if(limit
> cData
->modes
.new_limit
)
8213 limit
= cData
->modes
.new_limit
;
8214 else if(limit
== cData
->modes
.new_limit
)
8218 mod_chanmode_init(&change
);
8219 change
.modes_set
= MODE_LIMIT
;
8220 change
.new_limit
= limit
;
8221 mod_chanmode_announce(chanserv
, channel
, &change
);
8225 handle_new_channel(struct chanNode
*channel
)
8227 struct chanData
*cData
;
8229 if(!(cData
= channel
->channel_info
))
8232 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
8233 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
8235 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
8236 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
8240 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
8242 struct banData
*bData
;
8243 struct mod_chanmode
*change
;
8245 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
8250 if (chan
->channel_info
) {
8251 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
8253 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
))
8265 check_bans(struct userNode
*user
, const char *channel
)
8267 struct chanNode
*chan
;
8268 struct mod_chanmode change
;
8269 struct chanData
*cData
;
8270 struct banData
*bData
;
8272 if (!(chan
= GetChannel(channel
)))
8275 if(!(cData
= chan
->channel_info
))
8278 mod_chanmode_init(&change
);
8281 if(chan
->banlist
.used
< MAXBANS
)
8283 /* Not joining through a ban. */
8284 for(bData
= cData
->bans
;
8285 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
8286 bData
= bData
->next
);
8290 char kick_reason
[MAXLEN
];
8291 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8293 bData
->triggered
= now
;
8294 if(bData
!= cData
->bans
)
8296 /* Shuffle the ban to the head of the list. */
8298 bData
->next
->prev
= bData
->prev
;
8300 bData
->prev
->next
= bData
->next
;
8303 bData
->next
= cData
->bans
;
8306 cData
->bans
->prev
= bData
;
8308 cData
->bans
= bData
;
8311 change
.args
[0].mode
= MODE_BAN
;
8312 change
.args
[0].u
.hostmask
= bData
->mask
;
8313 mod_chanmode_announce(chanserv
, chan
, &change
);
8314 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8322 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
8325 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
8327 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
))
8334 /* Welcome to my worst nightmare. Warning: Read (or modify)
8335 the code below at your own risk. */
8337 handle_join(struct modeNode
*mNode
)
8339 struct mod_chanmode change
;
8340 struct userNode
*user
= mNode
->user
;
8341 struct chanNode
*channel
= mNode
->channel
;
8342 struct chanData
*cData
;
8343 struct userData
*uData
= NULL
;
8344 struct banData
*bData
;
8345 struct handle_info
*handle
;
8346 unsigned int modes
= 0, info
= 0;
8349 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8352 cData
= channel
->channel_info
;
8353 if(channel
->members
.used
> cData
->max
)
8354 cData
->max
= channel
->members
.used
;
8357 /* Check for bans. If they're joining through a ban, one of two
8359 * 1: Join during a netburst, by riding the break. Kick them
8360 * unless they have ops or voice in the channel.
8361 * 2: They're allowed to join through the ban (an invite in
8362 * ircu2.10, or a +e on Hybrid, or something).
8363 * If they're not joining through a ban, and the banlist is not
8364 * full, see if they're on the banlist for the channel. If so,
8367 if(user
->uplink
->burst
&& !mNode
->modes
)
8370 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
8372 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
8374 /* Riding a netburst. Naughty. */
8375 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
8382 if(user
->handle_info
)
8384 handle
= user
->handle_info
;
8387 uData
= GetTrueChannelAccess(cData
, handle
);
8392 mod_chanmode_init(&change
);
8395 /* TODO: maybe only people above inviteme level? -Rubin */
8396 /* We don't kick people with access */
8397 if(!uData
&& !channel_user_is_exempt(user
, channel
))
8399 if(channel
->banlist
.used
< MAXBANS
)
8401 /* Not joining through a ban. */
8402 for(bData
= cData
->bans
;
8403 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
8404 bData
= bData
->next
);
8408 char kick_reason
[MAXLEN
];
8409 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8411 bData
->triggered
= now
;
8412 if(bData
!= cData
->bans
)
8414 /* Shuffle the ban to the head of the list. */
8416 bData
->next
->prev
= bData
->prev
;
8418 bData
->prev
->next
= bData
->next
;
8421 bData
->next
= cData
->bans
;
8424 cData
->bans
->prev
= bData
;
8425 cData
->bans
= bData
;
8428 change
.args
[0].mode
= MODE_BAN
;
8429 change
.args
[0].u
.hostmask
= bData
->mask
;
8430 mod_chanmode_announce(chanserv
, channel
, &change
);
8431 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8437 /* ChanServ will not modify the limits in join-flooded channels.
8438 It will also skip DynLimit processing when the user (or srvx)
8439 is bursting in, because there are likely more incoming. */
8440 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8441 && !user
->uplink
->burst
8442 && !channel
->join_flooded
8443 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
8445 /* The user count has begun "bumping" into the channel limit,
8446 so set a timer to raise the limit a bit. Any previous
8447 timers are removed so three incoming users within the delay
8448 results in one limit change, not three. */
8450 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8451 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8454 /* Give automodes exept during join-floods */
8455 if(!channel
->join_flooded
)
8457 if(cData
->chOpts
[chAutomode
] == 'v')
8458 modes
|= MODE_VOICE
;
8459 else if(cData
->chOpts
[chAutomode
] == 'h')
8460 modes
|= MODE_HALFOP
;
8461 else if(cData
->chOpts
[chAutomode
] == 'o')
8462 modes
|= MODE_CHANOP
;
8465 greeting
= cData
->greeting
;
8466 if(user
->handle_info
)
8468 /* handle = user->handle_info; */
8470 if(IsHelper(user
) && !IsHelping(user
))
8473 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8475 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
8477 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8483 /* uData = GetTrueChannelAccess(cData, handle); */
8484 if(uData
&& !IsUserSuspended(uData
))
8486 /* non users getting automodes are handled above. */
8487 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
8489 /* just op everyone with access */
8490 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8491 modes
|= MODE_VOICE
;
8492 /* or do their access level */
8493 else if(uData
->access
>= UL_OP
)
8494 modes
|= MODE_CHANOP
;
8495 else if(uData
->access
>= UL_HALFOP
)
8496 modes
|= MODE_HALFOP
;
8497 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
8498 modes
|= MODE_VOICE
;
8500 if(uData
->access
>= UL_PRESENT
)
8501 cData
->visited
= now
;
8502 if(cData
->user_greeting
)
8503 greeting
= cData
->user_greeting
;
8505 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
8506 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
8514 /* If user joining normally (not during burst), apply op or voice,
8515 * and send greeting/userinfo as appropriate.
8517 if(!user
->uplink
->burst
)
8521 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8522 if(modes & MODE_CHANOP) {
8523 modes &= ~MODE_HALFOP;
8524 modes &= ~MODE_VOICE;
8527 change
.args
[0].mode
= modes
;
8528 change
.args
[0].u
.member
= mNode
;
8529 mod_chanmode_announce(chanserv
, channel
, &change
);
8532 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
8534 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
8540 chanserv_autojoin_channels(struct userNode
*user
)
8542 struct userData
*channel
;
8544 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8546 struct chanNode
*cn
;
8547 struct modeNode
*mn
;
8549 if(IsUserSuspended(channel
)
8550 || IsSuspended(channel
->channel
)
8551 || !(cn
= channel
->channel
->channel
))
8554 mn
= GetUserMode(cn
, user
);
8557 if(!IsUserSuspended(channel
)
8558 && IsUserAutoJoin(channel
)
8559 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8561 && !user
->uplink
->burst
)
8562 irc_svsjoin(chanserv
, user
, cn
);
8568 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
8570 struct mod_chanmode change
;
8571 struct userData
*channel
;
8572 unsigned int ii
, jj
, i
;
8574 if(!user
->handle_info
)
8577 mod_chanmode_init(&change
);
8579 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8581 struct chanNode
*cn
;
8582 struct chanData
*cData
;
8583 struct modeNode
*mn
;
8584 if(IsUserSuspended(channel
)
8585 || IsSuspended(channel
->channel
)
8586 || !(cn
= channel
->channel
->channel
))
8589 cData
= cn
->channel_info
;
8590 mn
= GetUserMode(cn
, user
);
8593 if(!IsUserSuspended(channel
)
8594 && IsUserAutoInvite(channel
)
8595 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8597 && !user
->uplink
->burst
)
8598 irc_invite(chanserv
, user
, cn
);
8602 if(channel
->access
>= UL_PRESENT
)
8603 channel
->channel
->visited
= now
;
8605 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8607 if(channel
->access
>= UL_OP
)
8608 change
.args
[0].mode
= MODE_CHANOP
;
8609 else if(channel
->access
>= UL_HALFOP
)
8610 change
.args
[0].mode
= MODE_HALFOP
;
8611 else if(channel
->access
>= UL_PEON
)
8612 change
.args
[0].mode
= MODE_VOICE
;
8614 change
.args
[0].mode
= 0;
8615 change
.args
[0].u
.member
= mn
;
8616 if(change
.args
[0].mode
)
8617 mod_chanmode_announce(chanserv
, cn
, &change
);
8620 channel
->seen
= now
;
8621 channel
->present
= 1;
8624 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8626 struct chanNode
*chan
= user
->channels
.list
[ii
]->channel
;
8627 struct banData
*ban
;
8629 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8630 || !chan
->channel_info
8631 || IsSuspended(chan
->channel_info
))
8633 if(protect_user(user
, chanserv
, chan
->channel_info
, true))
8635 for(jj
= 0; jj
< chan
->banlist
.used
; ++jj
)
8636 if(user_matches_glob(user
, chan
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8638 if(jj
< chan
->banlist
.used
)
8640 for(ban
= chan
->channel_info
->bans
; ban
; ban
= ban
->next
)
8642 char kick_reason
[MAXLEN
];
8643 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
8645 change
.args
[0].mode
= MODE_BAN
;
8646 change
.args
[0].u
.hostmask
= ban
->mask
;
8647 mod_chanmode_announce(chanserv
, chan
, &change
);
8648 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8649 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8650 ban
->triggered
= now
;
8655 if(IsSupportHelper(user
))
8657 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8659 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8661 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8667 if (user
->handle_info
->ignores
->used
) {
8668 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8669 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8673 if (user
->handle_info
->epithet
)
8674 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8676 /* process autojoin channels 5 seconds later as this sometimes
8677 happens before autohide */
8678 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8679 chanserv_autojoin_channels(user
);
8683 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
8685 struct chanData
*cData
;
8686 struct userData
*uData
;
8688 cData
= mn
->channel
->channel_info
;
8689 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8692 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8694 /* Allow for a bit of padding so that the limit doesn't
8695 track the user count exactly, which could get annoying. */
8696 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8698 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8699 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8703 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8705 scan_user_presence(uData
, mn
->user
);
8707 if (uData
->access
>= UL_PRESENT
)
8708 cData
->visited
= now
;
8711 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8714 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8715 if(find_handle_in_channel(chanserv_conf
.support_channels
.list
[ii
], mn
->user
->handle_info
, mn
->user
))
8717 if(ii
== chanserv_conf
.support_channels
.used
)
8718 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8723 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
8725 struct userData
*uData
;
8727 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8728 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8729 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8732 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8734 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8735 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8738 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8743 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
8745 struct chanData
*cData
;
8747 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8750 cData
= channel
->channel_info
;
8751 if(bad_topic(channel
, user
, channel
->topic
))
8752 { /* User doesnt have privs to set topics. Undo it */
8753 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8754 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8757 /* If there is a topic mask set, and the new topic doesnt match,
8758 * set the topic to mask + new_topic */
8759 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8761 char new_topic
[TOPICLEN
+1];
8762 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8765 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8766 /* and fall through to topicsnarf code below.. */
8768 else /* Topic couldnt fit into mask, was too long */
8770 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8771 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8772 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8776 /* With topicsnarf, grab the topic and save it as the default topic. */
8777 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8780 cData
->topic
= strdup(channel
->topic
);
8786 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
8788 struct mod_chanmode
*bounce
= NULL
;
8789 unsigned int bnc
, ii
;
8792 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8795 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8796 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8798 char correct
[MAXLEN
];
8799 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8800 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8801 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8803 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8805 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8807 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8808 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8811 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8814 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8815 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8816 if(bounce
->args
[bnc
].u
.member
)
8820 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8821 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8823 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8825 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8827 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8828 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8831 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8832 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8833 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8836 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8838 const char *ban
= change
->args
[ii
].u
.hostmask
;
8839 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8842 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8843 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8844 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8846 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8851 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8852 mod_chanmode_announce(chanserv
, channel
, bounce
);
8853 for(ii
= 0; ii
< change
->argc
; ++ii
)
8854 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8855 free((char*)bounce
->args
[ii
].u
.hostmask
);
8856 mod_chanmode_free(bounce
);
8861 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
8863 struct chanNode
*channel
;
8864 struct banData
*bData
;
8865 struct mod_chanmode change
;
8866 unsigned int ii
, jj
;
8867 char kick_reason
[MAXLEN
];
8869 mod_chanmode_init(&change
);
8871 change
.args
[0].mode
= MODE_BAN
;
8872 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8874 channel
= user
->channels
.list
[ii
]->channel
;
8875 /* Need not check for bans if they're opped or voiced. */
8876 /* TODO: does this make sense in automode v, h, and o? *
8877 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8878 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8880 /* Need not check for bans unless channel registration is active. */
8881 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8883 /* Look for a matching ban already on the channel. */
8884 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8885 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8887 /* Need not act if we found one. */
8888 if(jj
< channel
->banlist
.used
)
8890 /* don't kick someone on the userlist */
8891 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8893 /* Look for a matching ban in this channel. */
8894 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8896 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
8898 change
.args
[0].u
.hostmask
= bData
->mask
;
8899 mod_chanmode_announce(chanserv
, channel
, &change
);
8900 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8901 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8902 bData
->triggered
= now
;
8903 break; /* we don't need to check any more bans in the channel */
8908 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
8910 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8914 dict_remove2(handle_dnrs
, old_handle
, 1);
8915 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8916 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8921 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
8923 struct userNode
*h_user
;
8925 if(handle
->channels
)
8927 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8928 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8930 while(handle
->channels
)
8931 del_channel_user(handle
->channels
, 1);
8936 handle_server_link(UNUSED_ARG(struct server
*server
))
8938 struct chanData
*cData
;
8940 for(cData
= channelList
; cData
; cData
= cData
->next
)
8942 if(!IsSuspended(cData
))
8943 cData
->may_opchan
= 1;
8944 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8945 && !cData
->channel
->join_flooded
8946 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8947 < chanserv_conf
.adjust_threshold
))
8949 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8950 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8957 chanserv_conf_read(void)
8961 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
8962 struct mod_chanmode
*change
;
8963 struct string_list
*strlist
;
8964 struct chanNode
*chan
;
8967 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
8969 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
8972 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8973 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8974 chanserv_conf
.support_channels
.used
= 0;
8975 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
8977 for(ii
= 0; ii
< strlist
->used
; ++ii
)
8979 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8982 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
8984 channelList_append(&chanserv_conf
.support_channels
, chan
);
8987 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
8990 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8993 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
8995 channelList_append(&chanserv_conf
.support_channels
, chan
);
8997 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
8998 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
8999 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
9000 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
9001 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
9002 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
9003 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
9004 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
9005 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
9006 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
9007 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
9008 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
9009 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
9010 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
9011 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
9012 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
9013 str
= database_get_data(conf_node
, KEY_DNR_EXPIRE_FREQ
, RECDB_QSTRING
);
9014 chanserv_conf
.dnr_expire_frequency
= str
? ParseInterval(str
) : 3600;
9015 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
9016 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
9017 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
9018 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
9019 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
9020 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
9021 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
9022 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
9023 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
9025 NickChange(chanserv
, str
, 0);
9026 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
9027 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
9028 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
9029 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
9030 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
9031 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
9032 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
9033 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
9034 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
9035 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
9036 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
9037 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
9038 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
9039 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
9040 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
9041 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
9042 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
9043 god_timeout
= str
? ParseInterval(str
) : 60*15;
9044 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
9047 safestrncpy(mode_line
, str
, sizeof(mode_line
));
9048 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
9049 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
9050 && (change
->argc
< 2))
9052 chanserv_conf
.default_modes
= *change
;
9053 mod_chanmode_free(change
);
9055 str
= database_get_data(conf_node
, KEY_VALID_CHANNEL_REGEX
, RECDB_QSTRING
);
9056 if (chanserv_conf
.valid_channel_regex_set
)
9057 regfree(&chanserv_conf
.valid_channel_regex
);
9059 int err
= regcomp(&chanserv_conf
.valid_channel_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
9060 chanserv_conf
.valid_channel_regex_set
= !err
;
9061 if (err
) log_module(CS_LOG
, LOG_ERROR
, "Bad valid_channel_regex (error %d)", err
);
9063 chanserv_conf
.valid_channel_regex_set
= 0;
9065 free_string_list(chanserv_conf
.wheel
);
9066 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
9068 strlist
= string_list_copy(strlist
);
9071 static const char *list
[] = {
9072 "peer", "partall", "gline", /* "shun", */
9073 "nothing", "randjoin", "abusewhois", "kickall",
9074 "nickchange", "kill", "svsignore", "kickbanall",
9077 strlist
= alloc_string_list(ArrayLength(list
)-1);
9078 for(ii
=0; list
[ii
]; ii
++)
9079 string_list_append(strlist
, strdup(list
[ii
]));
9081 chanserv_conf
.wheel
= strlist
;
9083 free_string_list(chanserv_conf
.set_shows
);
9084 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
9086 strlist
= string_list_copy(strlist
);
9089 static const char *list
[] = {
9090 /* free form text */
9091 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9092 /* options based on user level */
9093 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9094 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
9095 /* multiple choice options */
9096 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
9097 /* binary options */
9098 "DynLimit", "NoDelete", "BanTimeout",
9102 strlist
= alloc_string_list(ArrayLength(list
)-1);
9103 for(ii
=0; list
[ii
]; ii
++)
9104 string_list_append(strlist
, strdup(list
[ii
]));
9106 chanserv_conf
.set_shows
= strlist
;
9107 /* We don't look things up now, in case the list refers to options
9108 * defined by modules initialized after this point. Just mark the
9109 * function list as invalid, so it will be initialized.
9111 set_shows_list
.used
= 0;
9113 free_string_list(chanserv_conf
.eightball
);
9114 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
9117 strlist
= string_list_copy(strlist
);
9121 strlist
= alloc_string_list(4);
9122 string_list_append(strlist
, strdup("Yes."));
9123 string_list_append(strlist
, strdup("No."));
9124 string_list_append(strlist
, strdup("Maybe so."));
9126 chanserv_conf
.eightball
= strlist
;
9128 free_string_list(chanserv_conf
.old_ban_names
);
9129 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
9131 strlist
= string_list_copy(strlist
);
9133 strlist
= alloc_string_list(2);
9134 chanserv_conf
.old_ban_names
= strlist
;
9135 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
9136 off_channel
= str
? atoi(str
) : 0;
9140 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
9143 struct note_type
*ntype
;
9146 if(!(obj
= GET_RECORD_OBJECT(rd
)))
9148 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
9151 if(!(ntype
= chanserv_create_note_type(key
)))
9153 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
9157 /* Figure out set access */
9158 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
9160 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9161 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
9163 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
9165 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
9166 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
9168 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
9170 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
9174 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
9175 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9176 ntype
->set_access
.min_opserv
= 0;
9179 /* Figure out visibility */
9180 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
9181 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9182 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
9183 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9184 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
9185 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
9186 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
9187 ntype
->visible_type
= NOTE_VIS_ALL
;
9189 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9191 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
9192 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
9196 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9198 struct handle_info
*handle
;
9199 struct userData
*uData
;
9200 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
9202 unsigned short access_level
, lastaccess
= 0;
9204 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9206 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
9210 access_level
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
9211 if(access_level
> UL_OWNER
)
9213 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
9217 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
9218 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
9219 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
9220 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
9221 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9222 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
9223 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
9224 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
9225 lastaccess
= lstacc
? atoi(lstacc
) : 0;
9227 handle
= get_handle_info(key
);
9230 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
9234 uData
= add_channel_user(chan
, handle
, access_level
, last_seen
, inf
, 0);
9235 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
9236 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
9238 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
9239 if (uData
->accessexpiry
> 0)
9240 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
9242 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
9243 if (uData
->clvlexpiry
> 0)
9244 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
9246 uData
->lastaccess
= lastaccess
;
9248 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
9250 if(uData
->expires
> now
)
9251 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
9253 uData
->flags
&= ~USER_SUSPENDED
;
9256 /* Upgrade: set autoop to the inverse of noautoop */
9257 if(chanserv_read_version
< 2)
9259 /* if noautoop is true, set autoop false, and vice versa */
9260 if(uData
->flags
& USER_NOAUTO_OP
)
9261 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
9263 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
9264 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
);
9270 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9272 struct banData
*bData
;
9273 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
9274 time_t set_time
, triggered_time
, expires_time
;
9276 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9278 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
9282 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
9283 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
9284 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
9285 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9286 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
9287 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
9288 if (!reason
|| !owner
)
9291 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
9292 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
9294 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
9296 expires_time
= set_time
+ atoi(s_duration
);
9300 if(!reason
|| (expires_time
&& (expires_time
< now
)))
9303 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
9306 static struct suspended
*
9307 chanserv_read_suspended(dict_t obj
)
9309 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
9313 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
9314 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9315 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
9316 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9317 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9318 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9319 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
9320 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
9321 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9322 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
9326 static struct giveownership
*
9327 chanserv_read_giveownership(dict_t obj
)
9329 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
9333 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
9334 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
9336 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
9338 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
9339 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
9341 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
9342 giveownership
->reason
= str
? strdup(str
) : NULL
;
9343 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9344 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9346 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9347 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
9348 return giveownership
;
9352 chanserv_channel_read(const char *key
, struct record_data
*hir
)
9354 struct suspended
*suspended
;
9355 struct giveownership
*giveownership
;
9356 struct mod_chanmode
*modes
;
9357 struct chanNode
*cNode
;
9358 struct chanData
*cData
;
9359 struct dict
*channel
, *obj
;
9360 char *str
, *argv
[10];
9364 channel
= hir
->d
.object
;
9366 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
9369 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
9372 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
9375 cData
= register_channel(cNode
, str
);
9378 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
9382 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
9384 enum levelOption lvlOpt
;
9385 enum charOption chOpt
;
9387 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
9388 cData
->flags
= atoi(str
);
9390 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9392 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
9394 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
9395 else if(levelOptions
[lvlOpt
].old_flag
)
9397 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9398 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
9400 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
9404 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9406 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
9408 cData
->chOpts
[chOpt
] = str
[0];
9411 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
9413 enum levelOption lvlOpt
;
9414 enum charOption chOpt
;
9417 cData
->flags
= base64toint(str
, 5);
9418 count
= strlen(str
+= 5);
9419 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9422 if(levelOptions
[lvlOpt
].old_flag
)
9424 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9425 lvl
= levelOptions
[lvlOpt
].flag_value
;
9427 lvl
= levelOptions
[lvlOpt
].default_value
;
9429 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
9431 case 'c': lvl
= UL_COOWNER
; break;
9432 case 'm': lvl
= UL_MANAGER
; break;
9433 case 'n': lvl
= UL_OWNER
+1; break;
9434 case 'o': lvl
= UL_OP
; break;
9435 case 'p': lvl
= UL_PEON
; break;
9436 case 'h': lvl
= UL_HALFOP
; break;
9437 case 'w': lvl
= UL_OWNER
; break;
9438 default: lvl
= 0; break;
9440 cData
->lvlOpts
[lvlOpt
] = lvl
;
9442 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9443 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
9446 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
9448 suspended
= chanserv_read_suspended(obj
);
9449 cData
->suspended
= suspended
;
9450 suspended
->cData
= cData
;
9451 /* We could use suspended->expires and suspended->revoked to
9452 * set the CHANNEL_SUSPENDED flag, but we don't. */
9454 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
9456 suspended
= calloc(1, sizeof(*suspended
));
9457 suspended
->issued
= 0;
9458 suspended
->revoked
= 0;
9459 suspended
->suspender
= strdup(str
);
9460 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
9461 suspended
->expires
= str
? atoi(str
) : 0;
9462 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
9463 suspended
->reason
= strdup(str
? str
: "No reason");
9464 suspended
->previous
= NULL
;
9465 cData
->suspended
= suspended
;
9466 suspended
->cData
= cData
;
9470 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9471 suspended
= NULL
; /* to squelch a warning */
9474 if(IsSuspended(cData
)) {
9475 if(suspended
->expires
> now
)
9476 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
9477 else if(suspended
->expires
)
9478 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9481 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
9483 giveownership
= chanserv_read_giveownership(obj
);
9484 cData
->giveownership
= giveownership
;
9487 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
9488 struct mod_chanmode change
;
9489 mod_chanmode_init(&change
);
9491 change
.args
[0].mode
= MODE_CHANOP
;
9492 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
9493 mod_chanmode_announce(chanserv
, cNode
, &change
);
9496 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
9497 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9498 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
9499 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9500 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
9501 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9502 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
9503 cData
->max
= str
? atoi(str
) : 0;
9504 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
9505 cData
->greeting
= str
? strdup(str
) : NULL
;
9506 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
9507 cData
->user_greeting
= str
? strdup(str
) : NULL
;
9508 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
9509 cData
->topic_mask
= str
? strdup(str
) : NULL
;
9510 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
9511 cData
->topic
= str
? strdup(str
) : NULL
;
9513 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
9514 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
9516 if(!IsSuspended(cData
)
9517 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
9518 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
9519 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
9520 cData
->modes
= *modes
;
9522 cData
->modes
.modes_set
|= MODE_REGISTERED
;
9523 if(cData
->modes
.argc
> 1)
9524 cData
->modes
.argc
= 1;
9525 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
9526 mod_chanmode_free(modes
);
9529 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
9530 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9531 user_read_helper(iter_key(it
), iter_data(it
), cData
);
9533 if(!cData
->users
&& !IsProtected(cData
))
9535 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
9536 unregister_channel(cData
, "has empty user list.");
9540 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
9541 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9542 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
9544 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
9545 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9547 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
9548 struct record_data
*rd
= iter_data(it
);
9549 const char *note
, *setter
;
9551 if(rd
->type
!= RECDB_OBJECT
)
9553 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9557 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9559 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9561 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9565 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9566 if(!setter
) setter
= "<unknown>";
9567 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9575 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9577 const char *setter
, *reason
, *str
;
9578 struct do_not_register
*dnr
;
9581 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9584 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9587 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9590 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9593 str
= database_get_data(hir
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9594 expiry
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9595 if(expiry
&& expiry
<= now
)
9597 dnr
= chanserv_add_dnr(key
, setter
, expiry
, reason
);
9600 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9602 dnr
->set
= atoi(str
);
9608 chanserv_version_read(struct dict
*section
)
9612 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9614 chanserv_read_version
= atoi(str
);
9615 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9619 chanserv_saxdb_read(struct dict
*database
)
9621 struct dict
*section
;
9624 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9625 chanserv_version_read(section
);
9627 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9628 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9629 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9631 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9632 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9633 chanserv_channel_read(iter_key(it
), iter_data(it
));
9635 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9636 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9637 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9643 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9645 int high_present
= 0;
9646 saxdb_start_record(ctx
, KEY_USERS
, 1);
9647 for(; uData
; uData
= uData
->next
)
9649 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9651 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9652 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9653 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9654 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9655 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9656 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9658 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9660 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9662 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9663 saxdb_end_record(ctx
);
9665 saxdb_end_record(ctx
);
9666 return high_present
;
9670 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9674 saxdb_start_record(ctx
, KEY_BANS
, 1);
9675 for(; bData
; bData
= bData
->next
)
9677 saxdb_start_record(ctx
, bData
->mask
, 0);
9678 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9679 if(bData
->triggered
)
9680 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9682 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9684 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9686 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9687 saxdb_end_record(ctx
);
9689 saxdb_end_record(ctx
);
9693 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9695 saxdb_start_record(ctx
, name
, 0);
9696 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9697 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9699 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9701 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9703 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9705 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9706 saxdb_end_record(ctx
);
9710 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9712 saxdb_start_record(ctx
, name
, 0);
9713 if(giveownership
->staff_issuer
)
9714 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9715 if(giveownership
->old_owner
)
9716 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9717 if(giveownership
->target
)
9718 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9719 if(giveownership
->target_access
)
9720 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9721 if(giveownership
->reason
)
9722 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9723 if(giveownership
->issued
)
9724 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9725 if(giveownership
->previous
)
9726 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9727 saxdb_end_record(ctx
);
9731 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9735 enum levelOption lvlOpt
;
9736 enum charOption chOpt
;
9738 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9740 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9741 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9743 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9744 if(channel
->registrar
)
9745 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9746 if(channel
->greeting
)
9747 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9748 if(channel
->user_greeting
)
9749 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9750 if(channel
->topic_mask
)
9751 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9752 if(channel
->suspended
)
9753 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9754 if(channel
->giveownership
)
9755 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9757 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9758 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9759 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9760 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9761 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9763 buf
[0] = channel
->chOpts
[chOpt
];
9765 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9767 saxdb_end_record(ctx
);
9769 if (channel
->maxsetinfo
)
9770 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9772 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9774 mod_chanmode_format(&channel
->modes
, buf
);
9775 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9778 high_present
= chanserv_write_users(ctx
, channel
->users
);
9779 chanserv_write_bans(ctx
, channel
->bans
);
9781 if(dict_size(channel
->notes
))
9785 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9786 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9788 struct note
*note
= iter_data(it
);
9789 saxdb_start_record(ctx
, iter_key(it
), 0);
9790 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9791 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9792 saxdb_end_record(ctx
);
9794 saxdb_end_record(ctx
);
9797 if(channel
->ownerTransfer
)
9798 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9799 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9800 saxdb_end_record(ctx
);
9804 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9808 saxdb_start_record(ctx
, ntype
->name
, 0);
9809 switch(ntype
->set_access_type
)
9811 case NOTE_SET_CHANNEL_ACCESS
:
9812 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9814 case NOTE_SET_CHANNEL_SETTER
:
9815 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9817 case NOTE_SET_PRIVILEGED
: default:
9818 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9821 switch(ntype
->visible_type
)
9823 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9824 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9825 case NOTE_VIS_PRIVILEGED
:
9826 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9828 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9829 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9830 saxdb_end_record(ctx
);
9834 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9836 struct do_not_register
*dnr
;
9837 dict_iterator_t it
, next
;
9839 for(it
= dict_first(dnrs
); it
; it
= next
)
9841 next
= iter_next(it
);
9842 dnr
= iter_data(it
);
9843 if(dnr
->expires
&& dnr
->expires
<= now
)
9845 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9847 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9850 dict_remove(dnrs
, iter_key(it
));
9851 saxdb_write_int(ctx
, KEY_EXPIRES
, dnr
->expires
);
9853 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9854 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9855 saxdb_end_record(ctx
);
9860 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9863 struct chanData
*channel
;
9865 /* Version Control*/
9866 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9867 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9868 saxdb_end_record(ctx
);
9871 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9872 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9873 chanserv_write_note_type(ctx
, iter_data(it
));
9874 saxdb_end_record(ctx
);
9877 saxdb_start_record(ctx
, KEY_DNR
, 1);
9878 write_dnrs_helper(ctx
, handle_dnrs
);
9879 write_dnrs_helper(ctx
, plain_dnrs
);
9880 write_dnrs_helper(ctx
, mask_dnrs
);
9881 saxdb_end_record(ctx
);
9884 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9885 for(channel
= channelList
; channel
; channel
= channel
->next
)
9886 chanserv_write_channel(ctx
, channel
);
9887 saxdb_end_record(ctx
);
9893 chanserv_db_cleanup(void) {
9895 unreg_part_func(handle_part
);
9897 unregister_channel(channelList
, "terminating.");
9898 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9899 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9900 free(chanserv_conf
.support_channels
.list
);
9901 dict_delete(handle_dnrs
);
9902 dict_delete(plain_dnrs
);
9903 dict_delete(mask_dnrs
);
9904 dict_delete(note_types
);
9905 free_string_list(chanserv_conf
.eightball
);
9906 free_string_list(chanserv_conf
.old_ban_names
);
9907 free_string_list(chanserv_conf
.wheel
);
9908 free_string_list(chanserv_conf
.set_shows
);
9909 free(set_shows_list
.list
);
9910 free(uset_shows_list
.list
);
9913 struct userData
*helper
= helperList
;
9914 helperList
= helperList
->next
;
9919 #if defined(GCC_VARMACROS)
9920 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
9921 #elif defined(C99_VARMACROS)
9922 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
9924 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9925 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9928 init_chanserv(const char *nick
)
9930 struct chanNode
*chan
;
9933 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9934 conf_register_reload(chanserv_conf_read
);
9937 reg_server_link_func(handle_server_link
);
9938 reg_new_channel_func(handle_new_channel
);
9939 reg_join_func(handle_join
);
9940 reg_part_func(handle_part
);
9941 reg_kick_func(handle_kick
);
9942 reg_topic_func(handle_topic
);
9943 reg_mode_change_func(handle_mode
);
9944 reg_nick_change_func(handle_nick_change
);
9945 reg_auth_func(handle_auth
);
9948 reg_handle_rename_func(handle_rename
);
9949 reg_unreg_func(handle_unreg
);
9951 handle_dnrs
= dict_new();
9952 dict_set_free_data(handle_dnrs
, free
);
9953 plain_dnrs
= dict_new();
9954 dict_set_free_data(plain_dnrs
, free
);
9955 mask_dnrs
= dict_new();
9956 dict_set_free_data(mask_dnrs
, free
);
9958 reg_svccmd_unbind_func(handle_svccmd_unbind
);
9959 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
9960 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+channel", NULL
);
9961 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9962 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
9963 DEFINE_COMMAND(dnrsearch
, 3, 0, "template", "noregister", NULL
);
9964 modcmd_register(chanserv_module
, "dnrsearch print", NULL
, 0, 0, NULL
);
9965 modcmd_register(chanserv_module
, "dnrsearch remove", NULL
, 0, 0, NULL
);
9966 modcmd_register(chanserv_module
, "dnrsearch count", NULL
, 0, 0, NULL
);
9967 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
9968 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9969 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9970 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
9971 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
9973 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9975 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
9976 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
9978 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9979 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9980 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9981 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9982 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9984 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
9985 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
9986 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
9987 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9988 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9989 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9990 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9992 DEFINE_COMMAND(levels
, 1, 0, NULL
);
9994 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9995 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
9996 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9997 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
9999 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10000 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
10001 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10002 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10003 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10004 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10005 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10006 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10007 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10008 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10010 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10011 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10012 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10013 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
10014 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
10015 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
10016 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
10017 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
10018 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
10019 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
10020 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
10021 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
10022 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10023 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10025 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10026 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10027 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10028 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10030 /* if you change dellamer access, see also places
10031 * like unbanme which have manager hardcoded. */
10032 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10033 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
10035 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
10037 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
10039 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10040 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10041 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10042 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10043 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10044 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10045 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10046 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10047 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10048 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10049 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10050 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10052 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
10053 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
10055 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
10056 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
10057 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
10058 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
10060 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10061 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10062 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
10063 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
10064 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
10066 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10067 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10068 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10069 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10070 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10071 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10072 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10073 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10074 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10075 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10076 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
10078 /* Channel options */
10079 DEFINE_CHANNEL_OPTION(defaulttopic
);
10080 DEFINE_CHANNEL_OPTION(topicmask
);
10081 DEFINE_CHANNEL_OPTION(greeting
);
10082 DEFINE_CHANNEL_OPTION(usergreeting
);
10083 DEFINE_CHANNEL_OPTION(modes
);
10084 DEFINE_CHANNEL_OPTION(enfops
);
10085 DEFINE_CHANNEL_OPTION(enfhalfops
);
10086 DEFINE_CHANNEL_OPTION(automode
);
10087 DEFINE_CHANNEL_OPTION(protect
);
10088 DEFINE_CHANNEL_OPTION(enfmodes
);
10089 DEFINE_CHANNEL_OPTION(enftopic
);
10090 DEFINE_CHANNEL_OPTION(pubcmd
);
10091 DEFINE_CHANNEL_OPTION(userinfo
);
10092 DEFINE_CHANNEL_OPTION(dynlimit
);
10093 DEFINE_CHANNEL_OPTION(topicsnarf
);
10094 DEFINE_CHANNEL_OPTION(nodelete
);
10095 DEFINE_CHANNEL_OPTION(toys
);
10096 DEFINE_CHANNEL_OPTION(setters
);
10097 DEFINE_CHANNEL_OPTION(topicrefresh
);
10098 DEFINE_CHANNEL_OPTION(resync
);
10099 DEFINE_CHANNEL_OPTION(ctcpreaction
);
10100 DEFINE_CHANNEL_OPTION(bantimeout
);
10101 DEFINE_CHANNEL_OPTION(inviteme
);
10102 DEFINE_CHANNEL_OPTION(unreviewed
);
10103 modcmd_register(chanserv_module
, "set unreviewed on", NULL
, 0, 0, "flags", "+helping", NULL
);
10104 modcmd_register(chanserv_module
, "set unreviewed off", NULL
, 0, 0, "flags", "+oper", NULL
);
10105 DEFINE_CHANNEL_OPTION(maxsetinfo
);
10106 if(off_channel
> 1)
10107 DEFINE_CHANNEL_OPTION(offchannel
);
10108 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
10110 /* Alias set topic to set defaulttopic for compatibility. */
10111 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
10114 DEFINE_USER_OPTION(autoinvite
);
10115 DEFINE_USER_OPTION(autojoin
);
10116 DEFINE_USER_OPTION(info
);
10117 DEFINE_USER_OPTION(autoop
);
10119 /* Alias uset autovoice to uset autoop. */
10120 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
10122 note_types
= dict_new();
10123 dict_set_free_data(note_types
, chanserv_deref_note_type
);
10126 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
10127 chanserv
= AddLocalUser(nick
, nick
, NULL
, "Channel Services", modes
);
10128 service_register(chanserv
)->trigger
= '!';
10129 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
10132 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
10134 if(chanserv_conf
.channel_expire_frequency
)
10135 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
10137 if(chanserv_conf
.dnr_expire_frequency
)
10138 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
10140 if(chanserv_conf
.ban_timeout_frequency
)
10141 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
10143 if(chanserv_conf
.refresh_period
)
10145 time_t next_refresh
;
10146 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
10147 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
10148 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
10151 if (autojoin_channels
&& chanserv
) {
10152 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
10153 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
10154 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
10158 reg_exit_func(chanserv_db_cleanup
);
10159 message_register_table(msgtab
);