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 %s a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
228 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
229 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
230 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
231 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
232 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
233 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
234 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
236 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
237 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
238 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
239 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
240 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
241 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
242 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
243 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
244 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
247 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
248 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
249 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
250 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
251 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
252 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
253 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
254 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
255 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
256 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
257 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
258 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
259 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
260 { "CSMSG_NO_EXTBANS", "$b%s$b is an extended ban, which are not allowed." },
261 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
262 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
263 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
264 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
265 { "CSMSG_BAD_BAN", "The given ban $b%s$b is invalid." },
267 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
269 /* Channel management */
270 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
271 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
272 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
274 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
275 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
276 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
277 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
278 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
279 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
280 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
282 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
283 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
284 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
285 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
286 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
287 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
288 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
289 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
290 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
291 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
292 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
293 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
294 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
295 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
296 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
297 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
298 { "CSMSG_SET_MODES", "$bModes $b %s" },
299 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
300 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
301 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
302 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
303 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
304 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
305 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
306 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
307 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
308 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
309 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
310 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
311 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
312 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
313 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
314 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
315 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
316 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
317 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
318 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
319 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
321 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
322 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
323 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
324 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
325 { "CSMSG_USET_INFO", "$bInfo $b %s" },
327 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
328 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
329 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
330 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
331 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
332 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
333 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
334 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
335 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
336 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
337 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
339 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
340 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
341 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
342 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
343 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
344 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
345 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
347 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
348 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
349 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
350 { "CSMSG_PROTECT_NONE", "No users will be protected." },
351 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
352 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
353 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
355 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
356 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
357 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
358 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
359 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
361 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
362 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
363 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
364 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
365 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
367 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
368 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
369 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
370 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
371 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
373 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
374 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
375 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
376 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
377 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
378 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
380 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
381 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
382 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
383 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
384 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
385 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
386 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
387 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
388 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
390 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
391 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
392 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
394 /* Channel userlist */
395 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
396 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
397 /* uncomment if needed to adujust styles (and change code below)
398 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
399 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
400 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
401 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
402 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
403 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
405 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
406 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
407 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
409 /* Channel note list */
410 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
411 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
412 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
413 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
414 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
415 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
416 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
417 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
418 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
419 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
420 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
421 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
422 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
423 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
424 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
426 /* Channel [un]suspension */
427 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
428 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
429 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
430 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
431 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
432 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
433 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
435 /* Access information */
436 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
437 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
438 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
439 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
440 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
441 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
442 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
443 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
444 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
445 { "CSMSG_SMURF_TARGET", "%s %s ($b%s$b)." },
446 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
447 { "CSMSG_UC_H_TITLE", "network helper" },
448 { "CSMSG_LC_H_TITLE", "support helper" },
449 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
451 /* Seen information */
452 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
453 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
454 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
455 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
457 /* Names information */
458 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
459 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
461 /* Channel information */
462 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
463 { "CSMSG_BAR", "----------------------------------------"},
464 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
465 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
466 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
467 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
468 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
469 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
470 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
471 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
472 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
473 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
474 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
475 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
476 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
477 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
478 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
479 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
480 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
481 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
482 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
483 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
484 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
485 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
486 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
487 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
488 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
489 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
491 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
492 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
493 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
494 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
495 { "CSMSG_PEEK_OPS", "$bOps:$b" },
496 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
497 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
499 /* Network information */
500 { "CSMSG_NETWORK_INFO", "Network Information:" },
501 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
502 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
503 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
504 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
505 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
506 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
507 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
508 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
511 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
512 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
513 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
515 /* Channel searches */
516 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
517 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
518 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
519 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
521 /* Channel configuration */
522 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
523 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
524 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
525 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
526 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
529 { "CSMSG_USER_OPTIONS", "User Options:" },
530 // { "CSMSG_USER_PROTECTED", "That user is protected." },
533 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
534 { "CSMSG_PING_RESPONSE", "Pong!" },
535 { "CSMSG_WUT_RESPONSE", "wut" },
536 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
537 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
538 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
539 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
540 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
541 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
542 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
543 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
544 { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
545 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
546 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
547 { "CSMSG_ROULETTE_CLICK", "Click" } ,
549 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
550 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
551 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
553 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
554 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
555 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
556 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
557 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
558 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
559 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
560 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
561 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
562 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
563 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
564 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
565 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
566 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
569 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
570 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
571 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
572 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
576 /* eject_user and unban_user flags */
577 #define ACTION_KICK 0x0001
578 #define ACTION_BAN 0x0002
579 #define ACTION_ADD_LAMER 0x0004
580 #define ACTION_ADD_TIMED_LAMER 0x0008
581 #define ACTION_UNBAN 0x0010
582 #define ACTION_DEL_LAMER 0x0020
584 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
585 #define MODELEN 40 + KEYLEN
589 #define CSFUNC_ARGS user, channel, argc, argv, cmd
591 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
592 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
593 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
594 reply("MSG_MISSING_PARAMS", argv[0]); \
598 DECLARE_LIST(dnrList
, struct do_not_register
*);
599 DEFINE_LIST(dnrList
, struct do_not_register
*)
601 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
603 struct userNode
*chanserv
;
606 extern struct string_list
*autojoin_channels
;
607 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
608 static struct log_type
*CS_LOG
;
609 struct adduserPending
* adduser_pendings
= NULL
;
610 unsigned int adduser_pendings_count
= 0;
611 unsigned long god_timeout
;
615 struct channelList support_channels
;
616 struct mod_chanmode default_modes
;
618 unsigned long db_backup_frequency
;
619 unsigned long channel_expire_frequency
;
620 unsigned long ban_timeout_frequency
;
621 unsigned long dnr_expire_frequency
;
624 unsigned int adjust_delay
;
625 long channel_expire_delay
;
626 unsigned int nodelete_level
;
628 unsigned int adjust_threshold
;
629 int join_flood_threshold
;
631 unsigned int greeting_length
;
632 unsigned int refresh_period
;
633 unsigned int giveownership_period
;
635 unsigned int max_owned
;
636 unsigned int max_chan_users
;
637 unsigned int max_chan_bans
; /* lamers */
638 unsigned int max_userinfo_length
;
639 unsigned int valid_channel_regex_set
: 1;
641 regex_t valid_channel_regex
;
643 struct string_list
*set_shows
;
644 struct string_list
*eightball
;
645 struct string_list
*old_ban_names
;
646 struct string_list
*wheel
;
648 const char *ctcp_short_ban_duration
;
649 const char *ctcp_long_ban_duration
;
651 const char *irc_operator_epithet
;
652 const char *network_helper_epithet
;
653 const char *support_helper_epithet
;
658 struct userNode
*user
;
659 struct userNode
*bot
;
660 struct chanNode
*channel
;
662 unsigned short lowest
;
663 unsigned short highest
;
664 struct userData
**users
;
665 struct helpfile_table table
;
668 enum note_access_type
670 NOTE_SET_CHANNEL_ACCESS
,
671 NOTE_SET_CHANNEL_SETTER
,
675 enum note_visible_type
678 NOTE_VIS_CHANNEL_USERS
,
682 struct io_fd
*socket_io_fd
;
683 extern struct cManagerNode cManager
;
687 enum note_access_type set_access_type
;
689 unsigned int min_opserv
;
690 unsigned short min_ulevel
;
692 enum note_visible_type visible_type
;
693 unsigned int max_length
;
700 struct note_type
*type
;
701 char setter
[NICKSERV_HANDLE_LEN
+1];
705 static unsigned int registered_channels
;
706 static unsigned int banCount
;
708 static const struct {
711 unsigned short level
;
713 } accessLevels
[] = { /* MUST be orderd less to most! */
714 { "pal", "Pal", UL_PEON
, '+' },
715 { "peon", "Peon", UL_PEON
, '+' },
716 { "halfop", "HalfOp", UL_HALFOP
, '%' },
717 { "op", "Op", UL_OP
, '@' },
718 { "manager", "Manager", UL_MANAGER
, '%' },
719 { "coowner", "Coowner", UL_COOWNER
, '*' },
720 { "owner", "Owner", UL_OWNER
, '!' },
721 { "helper", "BUG:", UL_HELPER
, 'X' }
724 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
725 static const struct {
728 unsigned short default_value
;
729 unsigned int old_idx
;
730 unsigned int old_flag
;
731 unsigned short flag_value
;
733 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
734 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
735 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
736 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
737 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
738 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
739 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
740 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
741 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
744 struct charOptionValues
{
747 } automodeValues
[] = {
748 { 'n', "CSMSG_AUTOMODE_NONE" },
749 { 'y', "CSMSG_AUTOMODE_NORMAL" },
750 { 'v', "CSMSG_AUTOMODE_VOICE" },
751 { 'h', "CSMSG_AUTOMODE_HOP" },
752 { 'o', "CSMSG_AUTOMODE_OP" },
753 { 'm', "CSMSG_AUTOMODE_MUTE" },
754 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
755 }, protectValues
[] = {
756 { 'a', "CSMSG_PROTECT_ALL" },
757 { 'e', "CSMSG_PROTECT_EQUAL" },
758 { 'l', "CSMSG_PROTECT_LOWER" },
759 { 'n', "CSMSG_PROTECT_NONE" }
761 { 'd', "CSMSG_TOYS_DISABLED" },
762 { 'n', "CSMSG_TOYS_PRIVATE" },
763 { 'p', "CSMSG_TOYS_PUBLIC" }
764 }, topicRefreshValues
[] = {
765 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
766 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
767 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
768 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
769 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
770 }, ctcpReactionValues
[] = {
771 { 'n', "CSMSG_CTCPREACTION_NONE" },
772 { 'k', "CSMSG_CTCPREACTION_KICK" },
773 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
774 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
775 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
776 }, banTimeoutValues
[] = {
777 { '0', "CSMSG_BANTIMEOUT_NONE" },
778 { '1', "CSMSG_BANTIMEOUT_10M" },
779 { '2', "CSMSG_BANTIMEOUT_2H" },
780 { '3', "CSMSG_BANTIMEOUT_4H" },
781 { '4', "CSMSG_BANTIMEOUT_1D" },
782 { '5', "CSMSG_BANTIMEOUT_1W" }
785 { 'n', "CSMSG_RESYNC_NEVER" },
786 { '1', "CSMSG_RESYNC_3_HOURS" },
787 { '2', "CSMSG_RESYNC_6_HOURS" },
788 { '3', "CSMSG_RESYNC_12_HOURS" },
789 { '4', "CSMSG_RESYNC_24_HOURS" }
792 static const struct {
796 unsigned int old_idx
;
798 struct charOptionValues
*values
;
800 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
801 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
802 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
803 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
804 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
805 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
806 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
809 struct userData
*helperList
;
810 struct chanData
*channelList
;
811 static struct module *chanserv_module
;
812 static unsigned int userCount
;
813 unsigned int chanserv_read_version
= 0; /* db version control */
815 #define CHANSERV_DB_VERSION 2
817 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
818 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
820 void sputsock(const char *text
, ...) PRINTF_LIKE(1, 2);
823 sputsock(const char *text
, ...)
829 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
831 va_start(arg_list
, text
);
832 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
834 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
836 log_replay(MAIN_LOG
, true, buffer
);
837 buffer
[pos
++] = '\n';
839 ioset_write(socket_io_fd
, buffer
, pos
);
843 user_level_from_name(const char *name
, unsigned short clamp_level
)
845 unsigned int level
= 0, ii
;
847 level
= strtoul(name
, NULL
, 10);
848 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
849 if(!irccasecmp(name
, accessLevels
[ii
].name
))
850 level
= accessLevels
[ii
].level
;
851 if(level
> clamp_level
)
857 user_level_name_from_level(int level
)
865 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
866 if(level
>= accessLevels
[ii
].level
)
867 highest
= accessLevels
[ii
].title
;
873 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
876 *minl
= strtoul(arg
, &sep
, 10);
884 *maxl
= strtoul(sep
+1, &sep
, 10);
892 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
894 struct userData
*uData
, **head
;
896 if(!channel
|| !handle
)
899 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
900 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
902 for(uData
= helperList
;
903 uData
&& uData
->handle
!= handle
;
904 uData
= uData
->next
);
908 uData
= calloc(1, sizeof(struct userData
));
909 uData
->handle
= handle
;
911 uData
->access
= UL_HELPER
;
917 uData
->next
= helperList
;
919 helperList
->prev
= uData
;
927 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
928 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
931 head
= &(channel
->users
);
934 if(uData
&& (uData
!= *head
))
936 /* Shuffle the user to the head of whatever list he was in. */
938 uData
->next
->prev
= uData
->prev
;
940 uData
->prev
->next
= uData
->next
;
946 (**head
).prev
= uData
;
953 /* Returns non-zero if user has at least the minimum access.
954 * exempt_owner is set when handling !set, so the owner can set things
957 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
959 struct userData
*uData
;
960 struct chanData
*cData
= channel
->channel_info
;
961 unsigned short minimum
= cData
->lvlOpts
[opt
];
964 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
967 if(minimum
<= uData
->access
)
969 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
974 /* Scan for other users authenticated to the same handle
975 still in the channel. If so, keep them listed as present.
977 user is optional, if not null, it skips checking that userNode
978 (for the handle_part function) */
980 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
984 if(IsSuspended(uData
->channel
)
985 || IsUserSuspended(uData
)
986 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
998 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, const char *text
, UNUSED_ARG(struct userNode
*bot
), UNUSED_ARG(unsigned int is_notice
), UNUSED_ARG(void *extra
))
1000 unsigned int eflags
, argc
;
1002 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
1004 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
1005 if(!channel
->channel_info
1006 || IsSuspended(channel
->channel_info
)
1008 || !ircncasecmp(text
, "ACTION ", 7))
1010 /* We dont punish people we know -Rubin
1011 * * Figure out the minimum level needed to CTCP the channel *
1013 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
1016 /* If they are a user of the channel, they are exempt */
1017 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
1019 /* We need to enforce against them; do so. */
1021 argv
[0] = (char*)text
;
1022 argv
[1] = user
->nick
;
1024 if(GetUserMode(channel
, user
))
1025 eflags
|= ACTION_KICK
;
1026 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
1027 default: case 'n': return;
1029 eflags
|= ACTION_KICK
;
1032 eflags
|= ACTION_BAN
;
1035 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1036 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
1039 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1040 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
1043 argv
[argc
++] = bad_ctcp_reason
;
1044 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
1048 chanserv_create_note_type(const char *name
)
1050 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
1051 strcpy(ntype
->name
, name
);
1053 dict_insert(note_types
, ntype
->name
, ntype
);
1058 chanserv_deref_note_type(void *data
)
1060 struct note_type
*ntype
= data
;
1062 if(--ntype
->refs
> 0)
1068 chanserv_flush_note_type(struct note_type
*ntype
)
1070 struct chanData
*cData
;
1071 for(cData
= channelList
; cData
; cData
= cData
->next
)
1072 dict_remove(cData
->notes
, ntype
->name
);
1076 chanserv_truncate_notes(struct note_type
*ntype
)
1078 struct chanData
*cData
;
1080 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1082 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1083 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1086 if(strlen(note
->note
) <= ntype
->max_length
)
1088 dict_remove2(cData
->notes
, ntype
->name
, 1);
1089 note
= realloc(note
, size
);
1090 note
->note
[ntype
->max_length
] = 0;
1091 dict_insert(cData
->notes
, ntype
->name
, note
);
1095 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1097 static struct note
*
1098 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1101 unsigned int len
= strlen(text
);
1103 if(len
> type
->max_length
) len
= type
->max_length
;
1104 note
= calloc(1, sizeof(*note
) + len
);
1106 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1107 memcpy(note
->note
, text
, len
);
1108 note
->note
[len
] = 0;
1109 dict_insert(channel
->notes
, type
->name
, note
);
1115 chanserv_free_note(void *data
)
1117 struct note
*note
= data
;
1119 chanserv_deref_note_type(note
->type
);
1120 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1124 static MODCMD_FUNC(cmd_createnote
) {
1125 struct note_type
*ntype
;
1126 unsigned int arg
= 1, existed
= 0, max_length
;
1128 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1131 ntype
= chanserv_create_note_type(argv
[arg
]);
1132 if(!irccasecmp(argv
[++arg
], "privileged"))
1135 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1136 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1138 else if(!irccasecmp(argv
[arg
], "channel"))
1140 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1143 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1146 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1147 ntype
->set_access
.min_ulevel
= ulvl
;
1149 else if(!irccasecmp(argv
[arg
], "setter"))
1151 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1155 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1159 if(!irccasecmp(argv
[++arg
], "privileged"))
1160 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1161 else if(!irccasecmp(argv
[arg
], "channel_users"))
1162 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1163 else if(!irccasecmp(argv
[arg
], "all"))
1164 ntype
->visible_type
= NOTE_VIS_ALL
;
1166 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1170 if((arg
+1) >= argc
) {
1171 reply("MSG_MISSING_PARAMS", argv
[0]);
1174 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1175 if(max_length
< 20 || max_length
> 450)
1177 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1180 if(existed
&& (max_length
< ntype
->max_length
))
1182 ntype
->max_length
= max_length
;
1183 chanserv_truncate_notes(ntype
);
1185 ntype
->max_length
= max_length
;
1188 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1190 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1195 dict_remove(note_types
, ntype
->name
);
1199 static MODCMD_FUNC(cmd_removenote
) {
1200 struct note_type
*ntype
;
1203 ntype
= dict_find(note_types
, argv
[1], NULL
);
1204 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1207 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1214 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1217 chanserv_flush_note_type(ntype
);
1219 dict_remove(note_types
, argv
[1]);
1220 reply("CSMSG_NOTE_DELETED", argv
[1]);
1225 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1229 if(orig
->modes_set
& change
->modes_clear
)
1231 if(orig
->modes_clear
& change
->modes_set
)
1233 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1234 && strcmp(orig
->new_key
, change
->new_key
))
1236 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1237 && (orig
->new_limit
!= change
->new_limit
))
1242 static char max_length_text
[MAXLEN
+1][16];
1244 static struct helpfile_expansion
1245 chanserv_expand_variable(const char *variable
)
1247 struct helpfile_expansion exp
;
1249 if(!irccasecmp(variable
, "notes"))
1252 exp
.type
= HF_TABLE
;
1253 exp
.value
.table
.length
= 1;
1254 exp
.value
.table
.width
= 3;
1255 exp
.value
.table
.flags
= 0;
1256 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1257 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1258 exp
.value
.table
.contents
[0][0] = "Note Type";
1259 exp
.value
.table
.contents
[0][1] = "Visibility";
1260 exp
.value
.table
.contents
[0][2] = "Max Length";
1261 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1263 struct note_type
*ntype
= iter_data(it
);
1266 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1267 row
= exp
.value
.table
.length
++;
1268 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1269 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1270 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1271 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1273 if(!max_length_text
[ntype
->max_length
][0])
1274 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1275 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1280 exp
.type
= HF_STRING
;
1281 exp
.value
.str
= NULL
;
1285 static struct chanData
*
1286 register_channel(struct chanNode
*cNode
, char *registrar
)
1288 struct chanData
*channel
;
1289 enum levelOption lvlOpt
;
1290 enum charOption chOpt
;
1292 channel
= calloc(1, sizeof(struct chanData
));
1294 channel
->notes
= dict_new();
1295 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1297 channel
->registrar
= strdup(registrar
);
1298 channel
->registered
= now
;
1299 channel
->visited
= now
;
1300 channel
->limitAdjusted
= now
;
1301 channel
->ownerTransfer
= now
;
1302 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1303 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1304 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1305 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1306 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1308 channel
->prev
= NULL
;
1309 channel
->next
= channelList
;
1312 channelList
->prev
= channel
;
1313 channelList
= channel
;
1314 registered_channels
++;
1316 channel
->channel
= cNode
;
1318 cNode
->channel_info
= channel
;
1323 static struct userData
*
1324 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access_level
, time_t seen
, const char *info
, time_t accessexpiry
)
1326 struct userData
*ud
;
1328 if(access_level
> UL_OWNER
)
1331 ud
= calloc(1, sizeof(*ud
));
1332 ud
->channel
= channel
;
1333 ud
->handle
= handle
;
1335 ud
->access
= access_level
;
1336 ud
->info
= info
? strdup(info
) : NULL
;
1337 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1342 ud
->next
= channel
->users
;
1344 channel
->users
->prev
= ud
;
1345 channel
->users
= ud
;
1347 channel
->userCount
++;
1351 ud
->u_next
= ud
->handle
->channels
;
1353 ud
->u_next
->u_prev
= ud
;
1354 ud
->handle
->channels
= ud
;
1356 ud
->flags
= USER_FLAGS_DEFAULT
;
1360 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1363 chanserv_expire_tempuser(void *data
)
1365 struct userData
*uData
= data
;
1369 handle
= strdup(uData
->handle
->handle
);
1370 if (uData
->accessexpiry
> 0) {
1371 if (uData
->present
) {
1372 struct userNode
*user
, *next_un
= NULL
;
1373 struct handle_info
*hi
;
1375 hi
= get_handle_info(handle
);
1376 for (user
= hi
->users
; user
; user
= next_un
) {
1377 struct mod_chanmode
*change
;
1378 struct modeNode
*mn
;
1379 unsigned int count
= 0;
1381 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1382 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1383 next_un
= user
->next_authed
;
1387 change
= mod_chanmode_alloc(2);
1388 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1389 change
->args
[count
++].u
.member
= mn
;
1392 change
->argc
= count
;
1393 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1395 mod_chanmode_free(change
);
1396 next_un
= user
->next_authed
;
1399 del_channel_user(uData
, 1);
1405 chanserv_expire_tempclvl(void *data
)
1407 struct userData
*uData
= data
;
1411 handle
= strdup(uData
->handle
->handle
);
1412 if (uData
->clvlexpiry
> 0) {
1413 int changemodes
= 0;
1414 unsigned int mode
= 0;
1416 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1418 mode
= MODE_REMOVE
| MODE_CHANOP
;
1419 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1421 mode
= MODE_REMOVE
| MODE_HALFOP
;
1425 if (uData
->present
) {
1426 struct userNode
*user
, *next_un
= NULL
;
1427 struct handle_info
*hi
;
1429 hi
= get_handle_info(handle
);
1430 for (user
= hi
->users
; user
; user
= next_un
) {
1431 struct mod_chanmode
*change
;
1432 struct modeNode
*mn
;
1433 unsigned int count
= 0;
1435 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1436 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1437 next_un
= user
->next_authed
;
1441 if (changemodes
== 0) {
1442 next_un
= user
->next_authed
;
1446 change
= mod_chanmode_alloc(2);
1447 change
->args
[count
].mode
= mode
;
1448 change
->args
[count
++].u
.member
= mn
;
1451 change
->argc
= count
;
1452 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1454 mod_chanmode_free(change
);
1455 next_un
= user
->next_authed
;
1459 uData
->access
= uData
->lastaccess
;
1460 uData
->lastaccess
= 0;
1461 uData
->clvlexpiry
= 0;
1467 del_channel_user(struct userData
*user
, int do_gc
)
1469 struct chanData
*channel
= user
->channel
;
1471 channel
->userCount
--;
1474 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1475 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1478 user
->prev
->next
= user
->next
;
1480 channel
->users
= user
->next
;
1482 user
->next
->prev
= user
->prev
;
1485 user
->u_prev
->u_next
= user
->u_next
;
1487 user
->handle
->channels
= user
->u_next
;
1489 user
->u_next
->u_prev
= user
->u_prev
;
1493 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1494 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1495 unregister_channel(channel
, "lost all users.");
1499 static struct adduserPending
*
1500 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1502 struct adduserPending
*ap
;
1503 ap
= calloc(1,sizeof(struct adduserPending
));
1504 ap
->channel
= channel
;
1507 ap
->created
= time(NULL
);
1509 /* ap->prev defaults to NULL already.. */
1510 ap
->next
= adduser_pendings
;
1511 if(adduser_pendings
)
1512 adduser_pendings
->prev
= ap
;
1513 adduser_pendings
= ap
;
1514 adduser_pendings_count
++;
1519 del_adduser_pending(struct adduserPending
*ap
)
1522 ap
->prev
->next
= ap
->next
;
1524 adduser_pendings
= ap
->next
;
1527 ap
->next
->prev
= ap
->prev
;
1531 static void expire_adduser_pending();
1533 /* find_adduser_pending(channel, user) will find an arbitrary record
1534 * from user, channel, or user and channel.
1535 * if user or channel are NULL, they will match any records.
1537 static struct adduserPending
*
1538 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1540 struct adduserPending
*ap
;
1542 expire_adduser_pending(); /* why not here.. */
1544 if(!channel
&& !user
) /* 2 nulls matches all */
1545 return(adduser_pendings
);
1546 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1548 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1555 /* Remove all pendings for a user or channel
1557 * called in nickserv.c DelUser() and proto-* unregister_channel()
1560 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1562 struct adduserPending
*ap
;
1564 /* So this is a bit wastefull, i hate dealing with linked lists.
1565 * if its a problem we'll rewrite it right */
1566 while((ap
= find_adduser_pending(channel
, user
))) {
1567 del_adduser_pending(ap
);
1571 /* Called from nickserv.c cmd_auth after someone auths */
1573 process_adduser_pending(struct userNode
*user
)
1575 struct adduserPending
*ap
;
1576 if(!user
->handle_info
)
1577 return; /* not associated with an account */
1578 while((ap
= find_adduser_pending(NULL
, user
)))
1580 struct userData
*actee
;
1581 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1583 /* Already on the userlist. do nothing*/
1587 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1588 scan_user_presence(actee
, NULL
);
1590 del_adduser_pending(ap
);
1595 expire_adduser_pending()
1597 struct adduserPending
*ap
, *ap_next
;
1598 ap
= adduser_pendings
;
1601 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1603 ap_next
= ap
->next
; /* save next */
1604 del_adduser_pending(ap
); /* free and relink */
1605 ap
= ap_next
; /* advance */
1612 static void expire_ban(void *data
);
1615 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1618 unsigned int ii
, l1
, l2
;
1623 bd
= malloc(sizeof(struct banData
));
1625 bd
->channel
= channel
;
1627 bd
->triggered
= triggered
;
1628 bd
->expires
= expires
;
1630 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1632 extern const char *hidden_host_suffix
;
1633 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1637 l2
= strlen(old_name
);
1640 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1642 new_mask
= alloca(MAXLEN
);
1643 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1646 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1648 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1649 bd
->reason
= strdup(reason
);
1652 timeq_add(expires
, expire_ban
, bd
);
1655 bd
->next
= channel
->bans
; /* lamers */
1657 channel
->bans
->prev
= bd
;
1659 channel
->banCount
++;
1666 del_channel_ban(struct banData
*ban
)
1668 ban
->channel
->banCount
--;
1672 ban
->prev
->next
= ban
->next
;
1674 ban
->channel
->bans
= ban
->next
;
1677 ban
->next
->prev
= ban
->prev
;
1680 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1689 expire_ban(void *data
) /* lamer.. */
1691 struct banData
*bd
= data
;
1692 if(!IsSuspended(bd
->channel
))
1694 struct banList bans
;
1695 struct mod_chanmode change
;
1697 bans
= bd
->channel
->channel
->banlist
;
1698 mod_chanmode_init(&change
);
1699 for(ii
=0; ii
<bans
.used
; ii
++)
1701 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1704 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1705 change
.args
[0].u
.hostmask
= bd
->mask
;
1706 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1712 del_channel_ban(bd
);
1715 static void chanserv_expire_suspension(void *data
);
1718 unregister_channel(struct chanData
*channel
, const char *reason
)
1720 struct mod_chanmode change
;
1721 char msgbuf
[MAXLEN
];
1723 /* After channel unregistration, the following must be cleaned
1725 - Channel information.
1727 - Channel bans. (lamers)
1728 - Channel suspension data.
1729 - adduser_pending data.
1730 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1736 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1740 mod_chanmode_init(&change
);
1741 change
.modes_clear
|= MODE_REGISTERED
;
1742 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1745 wipe_adduser_pending(channel
->channel
, NULL
);
1747 while(channel
->users
)
1748 del_channel_user(channel
->users
, 0);
1750 while(channel
->bans
)
1751 del_channel_ban(channel
->bans
);
1753 free(channel
->topic
);
1754 free(channel
->registrar
);
1755 free(channel
->greeting
);
1756 free(channel
->user_greeting
);
1757 free(channel
->topic_mask
);
1760 channel
->prev
->next
= channel
->next
;
1762 channelList
= channel
->next
;
1765 channel
->next
->prev
= channel
->prev
;
1767 if(channel
->suspended
)
1769 struct chanNode
*cNode
= channel
->channel
;
1770 struct suspended
*suspended
, *next_suspended
;
1772 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1774 next_suspended
= suspended
->previous
;
1775 free(suspended
->suspender
);
1776 free(suspended
->reason
);
1777 if(suspended
->expires
)
1778 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1783 cNode
->channel_info
= NULL
;
1785 channel
->channel
->channel_info
= NULL
;
1787 dict_delete(channel
->notes
);
1788 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1789 if(!IsSuspended(channel
))
1790 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1791 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1792 UnlockChannel(channel
->channel
);
1794 registered_channels
--;
1798 expire_channels(UNUSED_ARG(void *data
))
1800 struct chanData
*channel
, *next
;
1801 struct userData
*user
;
1802 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1804 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1805 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1807 for(channel
= channelList
; channel
; channel
= next
)
1809 next
= channel
->next
;
1811 /* See if the channel can be expired. */
1812 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1813 || IsProtected(channel
))
1816 /* Make sure there are no high-ranking users still in the channel. */
1817 for(user
=channel
->users
; user
; user
=user
->next
)
1818 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1823 /* Unregister the channel */
1824 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1825 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1826 unregister_channel(channel
, "registration expired.");
1829 if(chanserv_conf
.channel_expire_frequency
)
1830 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1834 expire_dnrs(UNUSED_ARG(void *data
))
1836 dict_iterator_t it
, next
;
1837 struct do_not_register
*dnr
;
1839 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
1841 dnr
= iter_data(it
);
1842 next
= iter_next(it
);
1843 if(dnr
->expires
&& dnr
->expires
<= now
)
1844 dict_remove(handle_dnrs
, dnr
->chan_name
+ 1);
1846 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
1848 dnr
= iter_data(it
);
1849 next
= iter_next(it
);
1850 if(dnr
->expires
&& dnr
->expires
<= now
)
1851 dict_remove(plain_dnrs
, dnr
->chan_name
+ 1);
1853 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
1855 dnr
= iter_data(it
);
1856 next
= iter_next(it
);
1857 if(dnr
->expires
&& dnr
->expires
<= now
)
1858 dict_remove(mask_dnrs
, dnr
->chan_name
+ 1);
1861 if(chanserv_conf
.dnr_expire_frequency
)
1862 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
1866 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1868 char protect
= channel
->chOpts
[chProtect
];
1869 struct userData
*cs_victim
, *cs_aggressor
;
1871 /* If victim access level is greater than set invitelevel, don't let
1872 * us kick them, but don't consider it punishment if someone else does
1876 if(victim
== aggressor
)
1878 /* Don't protect if the victim isn't authenticated (because they
1879 can't be a channel user), unless we are to protect non-users
1882 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1884 /* If they have enough access to invite themselvs through a ban,
1885 * and its us kicking them, don't. -Rubin */
1886 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1892 if(protect
!= 'a' && !cs_victim
)
1895 /* Protect if the aggressor isn't a user because at this point,
1896 the aggressor can only be less than or equal to the victim. */
1898 /* Not protected from chanserv except above */
1899 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1900 if(aggressor
== chanserv
)
1903 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1907 /* If the aggressor was a user, then the victim can't be helped. */
1914 if(cs_victim
->access
> cs_aggressor
->access
)
1919 if(cs_victim
->access
>= cs_aggressor
->access
)
1928 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1930 struct chanData
*cData
= channel
->channel_info
;
1931 struct userData
*cs_victim
;
1933 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1934 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1935 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1938 reply("CSMSG_OPBY_LOCKED");
1940 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1948 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1950 struct chanData
*cData
= channel
->channel_info
;
1951 struct userData
*cs_victim
;
1953 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1954 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1955 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1957 reply("CSMSG_HOPBY_LOCKED");
1966 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1968 if(IsService(victim
))
1970 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1974 if(protect_user(victim
, user
, channel
->channel_info
, false))
1976 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1984 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1986 if(IsService(victim
))
1988 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1992 if(protect_user(victim
, user
, channel
->channel_info
, false))
1994 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2001 static struct do_not_register
*
2002 chanserv_add_dnr(const char *chan_name
, const char *setter
, time_t expires
, const char *reason
)
2004 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
2005 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
2006 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
2007 strcpy(dnr
->reason
, reason
);
2009 dnr
->expires
= expires
;
2010 if(dnr
->chan_name
[0] == '*')
2011 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
2012 else if(strpbrk(dnr
->chan_name
, "*?"))
2013 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
2015 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
2019 static struct dnrList
2020 chanserv_find_dnrs(const char *chan_name
, const char *handle
, unsigned int max
)
2022 struct dnrList list
;
2023 dict_iterator_t it
, next
;
2024 struct do_not_register
*dnr
;
2026 dnrList_init(&list
);
2028 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
2030 if(dnr
->expires
&& dnr
->expires
<= now
)
2031 dict_remove(handle_dnrs
, handle
);
2032 else if (list
.used
< max
)
2033 dnrList_append(&list
, dnr
);
2036 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
2038 if(dnr
->expires
&& dnr
->expires
<= now
)
2039 dict_remove(plain_dnrs
, chan_name
);
2040 else if (list
.used
< max
)
2041 dnrList_append(&list
, dnr
);
2045 for(it
= dict_first(mask_dnrs
); it
&& list
.used
< max
; it
= next
)
2047 next
= iter_next(it
);
2048 if(!match_ircglob(chan_name
, iter_key(it
)))
2050 dnr
= iter_data(it
);
2051 if(dnr
->expires
&& dnr
->expires
<= now
)
2052 dict_remove(mask_dnrs
, iter_key(it
));
2054 dnrList_append(&list
, dnr
);
2060 static int dnr_print_func(struct do_not_register
*dnr
, void *extra
)
2062 struct userNode
*user
;
2063 char buf1
[INTERVALLEN
];
2064 char buf2
[INTERVALLEN
];
2068 strftime(buf1
, sizeof(buf1
), "%d %b %Y", localtime(&dnr
->set
));
2071 strftime(buf2
, sizeof(buf2
), "%d %b %Y", localtime(&dnr
->expires
));
2072 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET_EXPIRES", dnr
->chan_name
, buf1
, dnr
->setter
, buf2
, dnr
->reason
);
2076 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf1
, dnr
->setter
, dnr
->reason
);
2079 send_message(user
, chanserv
, "CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2084 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
2086 struct dnrList list
;
2089 list
= chanserv_find_dnrs(chan_name
, handle
, UINT_MAX
);
2090 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
2091 dnr_print_func(list
.list
[ii
], user
);
2093 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2098 struct do_not_register
*
2099 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2101 struct dnrList list
;
2102 struct do_not_register
*dnr
;
2104 list
= chanserv_find_dnrs(chan_name
, handle
? handle
->handle
: NULL
, 1);
2105 dnr
= list
.used
? list
.list
[0] : NULL
;
2110 static unsigned int send_dnrs(struct userNode
*user
, dict_t dict
)
2112 struct do_not_register
*dnr
;
2113 dict_iterator_t it
, next
;
2114 unsigned int matches
= 0;
2116 for(it
= dict_first(dict
); it
; it
= next
)
2118 dnr
= iter_data(it
);
2119 next
= iter_next(it
);
2120 if(dnr
->expires
&& dnr
->expires
<= now
)
2122 dict_remove(dict
, iter_key(it
));
2125 dnr_print_func(dnr
, user
);
2132 static CHANSERV_FUNC(cmd_noregister
)
2136 time_t expiry
, duration
;
2137 unsigned int matches
;
2141 reply("CSMSG_DNR_SEARCH_RESULTS");
2142 matches
= send_dnrs(user
, handle_dnrs
);
2143 matches
+= send_dnrs(user
, plain_dnrs
);
2144 matches
+= send_dnrs(user
, mask_dnrs
);
2146 reply("MSG_MATCH_COUNT", matches
);
2148 reply("MSG_NO_MATCHES");
2154 if(!IsChannelName(target
) && (*target
!= '*'))
2156 reply("CSMSG_NOT_DNR", target
);
2164 reply("MSG_INVALID_DURATION", argv
[2]);
2168 if(!strcmp(argv
[2], "0"))
2170 else if((duration
= ParseInterval(argv
[2])))
2171 expiry
= now
+ duration
;
2174 reply("MSG_INVALID_DURATION", argv
[2]);
2178 reason
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
2180 if((*target
== '*') && !get_handle_info(target
+ 1))
2182 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2185 chanserv_add_dnr(target
, user
->handle_info
->handle
, expiry
, reason
);
2186 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2190 reply("CSMSG_DNR_SEARCH_RESULTS");
2191 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2194 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2196 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2198 reply("MSG_NO_MATCHES");
2202 static CHANSERV_FUNC(cmd_allowregister
)
2204 const char *chan_name
= argv
[1];
2206 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2207 || dict_remove(plain_dnrs
, chan_name
)
2208 || dict_remove(mask_dnrs
, chan_name
))
2210 reply("CSMSG_DNR_REMOVED", chan_name
);
2213 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2218 struct userNode
*source
;
2222 time_t min_set
, max_set
;
2223 time_t min_expires
, max_expires
;
2228 dnr_search_matches(const struct do_not_register
*dnr
, const struct dnr_search
*search
)
2230 return !((dnr
->set
< search
->min_set
)
2231 || (dnr
->set
> search
->max_set
)
2232 || (dnr
->expires
< search
->min_expires
)
2233 || (search
->max_expires
2234 && ((dnr
->expires
== 0)
2235 || (dnr
->expires
> search
->max_expires
)))
2236 || (search
->chan_mask
2237 && !match_ircglob(dnr
->chan_name
, search
->chan_mask
))
2238 || (search
->setter_mask
2239 && !match_ircglob(dnr
->setter
, search
->setter_mask
))
2240 || (search
->reason_mask
2241 && !match_ircglob(dnr
->reason
, search
->reason_mask
)));
2244 static struct dnr_search
*
2245 dnr_search_create(struct userNode
*user
, struct svccmd
*cmd
, unsigned int argc
, char *argv
[])
2247 struct dnr_search
*discrim
;
2250 discrim
= calloc(1, sizeof(*discrim
));
2251 discrim
->source
= user
;
2252 discrim
->chan_mask
= NULL
;
2253 discrim
->setter_mask
= NULL
;
2254 discrim
->reason_mask
= NULL
;
2255 discrim
->max_set
= INT_MAX
;
2256 discrim
->limit
= 50;
2258 for(ii
=0; ii
<argc
; ++ii
)
2262 reply("MSG_MISSING_PARAMS", argv
[ii
]);
2265 else if(0 == irccasecmp(argv
[ii
], "channel"))
2267 discrim
->chan_mask
= argv
[++ii
];
2269 else if(0 == irccasecmp(argv
[ii
], "setter"))
2271 discrim
->setter_mask
= argv
[++ii
];
2273 else if(0 == irccasecmp(argv
[ii
], "reason"))
2275 discrim
->reason_mask
= argv
[++ii
];
2277 else if(0 == irccasecmp(argv
[ii
], "limit"))
2279 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 0);
2281 else if(0 == irccasecmp(argv
[ii
], "set"))
2283 const char *cmp
= argv
[++ii
];
2286 discrim
->min_set
= now
- ParseInterval(cmp
+ 2);
2288 discrim
->min_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2289 } else if(cmp
[0] == '=') {
2290 discrim
->min_set
= discrim
->max_set
= now
- ParseInterval(cmp
+ 1);
2291 } else if(cmp
[0] == '>') {
2293 discrim
->max_set
= now
- ParseInterval(cmp
+ 2);
2295 discrim
->max_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2297 discrim
->max_set
= now
- (ParseInterval(cmp
) - 1);
2300 else if(0 == irccasecmp(argv
[ii
], "expires"))
2302 const char *cmp
= argv
[++ii
];
2305 discrim
->max_expires
= now
+ ParseInterval(cmp
+ 2);
2307 discrim
->max_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2308 } else if(cmp
[0] == '=') {
2309 discrim
->min_expires
= discrim
->max_expires
= now
+ ParseInterval(cmp
+ 1);
2310 } else if(cmp
[0] == '>') {
2312 discrim
->min_expires
= now
+ ParseInterval(cmp
+ 2);
2314 discrim
->min_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2316 discrim
->min_expires
= now
+ (ParseInterval(cmp
) - 1);
2321 reply("MSG_INVALID_CRITERIA", argv
[ii
]);
2332 typedef int (*dnr_search_func
)(struct do_not_register
*match
, void *extra
);
2335 dnr_search(struct dnr_search
*discrim
, dnr_search_func dsf
, void *data
)
2337 struct do_not_register
*dnr
;
2338 dict_iterator_t next
;
2343 /* Initialize local variables. */
2346 if(discrim
->chan_mask
)
2348 int shift
= (discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*') ? 2 : 0;
2349 if('\0' == discrim
->chan_mask
[shift
+ strcspn(discrim
->chan_mask
+shift
, "*?")])
2353 if(target_fixed
&& discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*')
2355 /* Check against account-based DNRs. */
2356 dnr
= dict_find(handle_dnrs
, discrim
->chan_mask
+ 2, NULL
);
2357 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2360 else if(target_fixed
)
2362 /* Check against channel-based DNRs. */
2363 dnr
= dict_find(plain_dnrs
, discrim
->chan_mask
, NULL
);
2364 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2369 /* Exhaustively search account DNRs. */
2370 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
2372 next
= iter_next(it
);
2373 dnr
= iter_data(it
);
2374 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2378 /* Do the same for channel DNRs. */
2379 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
2381 next
= iter_next(it
);
2382 dnr
= iter_data(it
);
2383 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2387 /* Do the same for wildcarded channel DNRs. */
2388 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
2390 next
= iter_next(it
);
2391 dnr
= iter_data(it
);
2392 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2400 dnr_remove_func(struct do_not_register
*match
, void *extra
)
2402 struct userNode
*user
;
2405 chan_name
= alloca(strlen(match
->chan_name
) + 1);
2406 strcpy(chan_name
, match
->chan_name
);
2408 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2409 || dict_remove(plain_dnrs
, chan_name
)
2410 || dict_remove(mask_dnrs
, chan_name
))
2412 send_message(user
, chanserv
, "CSMSG_DNR_REMOVED", chan_name
);
2418 dnr_count_func(struct do_not_register
*match
, void *extra
)
2420 return 0; (void)match
; (void)extra
;
2423 static MODCMD_FUNC(cmd_dnrsearch
)
2425 struct dnr_search
*discrim
;
2426 dnr_search_func action
;
2427 struct svccmd
*subcmd
;
2428 unsigned int matches
;
2431 sprintf(buf
, "dnrsearch %s", argv
[1]);
2432 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
2435 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2438 if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
2440 if(!irccasecmp(argv
[1], "print"))
2441 action
= dnr_print_func
;
2442 else if(!irccasecmp(argv
[1], "remove"))
2443 action
= dnr_remove_func
;
2444 else if(!irccasecmp(argv
[1], "count"))
2445 action
= dnr_count_func
;
2448 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2452 discrim
= dnr_search_create(user
, cmd
, argc
-2, argv
+2);
2456 if(action
== dnr_print_func
)
2457 reply("CSMSG_DNR_SEARCH_RESULTS");
2458 matches
= dnr_search(discrim
, action
, user
);
2460 reply("MSG_MATCH_COUNT", matches
);
2462 reply("MSG_NO_MATCHES");
2468 chanserv_get_owned_count(struct handle_info
*hi
)
2470 struct userData
*cList
;
2473 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2474 if(cList
->access
== UL_OWNER
)
2479 static CHANSERV_FUNC(cmd_register
)
2481 struct handle_info
*handle
;
2482 struct chanData
*cData
;
2483 struct modeNode
*mn
;
2484 char reason
[MAXLEN
];
2486 unsigned int new_channel
, force
=0;
2487 struct do_not_register
*dnr
;
2490 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2491 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2497 if(channel
->channel_info
)
2499 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2503 if(channel
->bad_channel
)
2505 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2509 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2511 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2516 chan_name
= channel
->name
;
2522 reply("MSG_MISSING_PARAMS", cmd
->name
);
2523 svccmd_send_help_brief(user
, chanserv
, cmd
);
2526 if(!IsChannelName(argv
[1]))
2528 reply("MSG_NOT_CHANNEL_NAME");
2532 if(opserv_bad_channel(argv
[1]))
2534 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2539 chan_name
= argv
[1];
2542 if(argc
>= (new_channel
+2))
2544 if(!IsHelping(user
))
2546 reply("CSMSG_PROXY_FORBIDDEN");
2550 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2552 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2553 dnr
= chanserv_is_dnr(chan_name
, handle
);
2555 /* Check if they are over the limit.. */
2556 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2558 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2565 handle
= user
->handle_info
;
2566 dnr
= chanserv_is_dnr(chan_name
, handle
);
2567 /* Check if they are over the limit.. */
2568 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2570 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2573 /* Check if another service is in the channel */
2575 for(n
= 0; n
< channel
->members
.used
; n
++)
2577 mn
= channel
->members
.list
[n
];
2578 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2580 reply("CSMSG_ANOTHER_SERVICE");
2587 if(!IsHelping(user
))
2588 reply("CSMSG_DNR_CHANNEL", chan_name
);
2590 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2594 /* now handled above for message specilization *
2595 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2597 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2602 if (chanserv_conf
.valid_channel_regex_set
) {
2603 int err
= regexec(&chanserv_conf
.valid_channel_regex
, chan_name
, 0, 0, 0);
2606 buff
[regerror(err
, &chanserv_conf
.valid_channel_regex
, buff
, sizeof(buff
))] = 0;
2607 log_module(CS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
2609 if(err
== REG_NOMATCH
) {
2610 reply("CSMSG_ILLEGAL_CHANNEL", chan_name
);
2616 channel
= AddChannel(chan_name
, now
, NULL
, NULL
, NULL
);
2618 cData
= register_channel(channel
, user
->handle_info
->handle
);
2619 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2620 cData
->modes
= chanserv_conf
.default_modes
;
2622 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2623 if (IsOffChannel(cData
))
2625 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2629 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2630 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2631 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2633 mod_chanmode_announce(chanserv
, channel
, change
);
2634 mod_chanmode_free(change
);
2637 /* Initialize the channel's max user record. */
2638 cData
->max
= channel
->members
.used
;
2639 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2641 if(handle
!= user
->handle_info
)
2642 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2645 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2646 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2647 handle
->handle
, user
->handle_info
->handle
);
2652 make_confirmation_string(struct userData
*uData
)
2654 static char strbuf
[16];
2659 for(src
= uData
->handle
->handle
; *src
; )
2660 accum
= accum
* 31 + toupper(*src
++);
2662 for(src
= uData
->channel
->channel
->name
; *src
; )
2663 accum
= accum
* 31 + toupper(*src
++);
2664 sprintf(strbuf
, "%08x", accum
);
2668 static CHANSERV_FUNC(cmd_unregister
)
2671 char reason
[MAXLEN
];
2672 struct chanData
*cData
;
2673 struct userData
*uData
;
2675 cData
= channel
->channel_info
;
2678 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2682 uData
= GetChannelUser(cData
, user
->handle_info
);
2683 if(!uData
|| (uData
->access
< UL_OWNER
))
2685 reply("CSMSG_NO_ACCESS");
2689 if(IsProtected(cData
))
2691 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2695 if(!IsHelping(user
))
2697 const char *confirm_string
;
2698 if(IsSuspended(cData
))
2700 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2703 confirm_string
= make_confirmation_string(uData
);
2704 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2706 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2711 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2712 name
= strdup(channel
->name
);
2713 unregister_channel(cData
, reason
);
2714 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2715 reply("CSMSG_UNREG_SUCCESS", name
);
2721 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2723 extern struct userNode
*spamserv
;
2724 struct mod_chanmode
*change
;
2726 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2728 change
= mod_chanmode_alloc(2);
2730 change
->args
[0].mode
= MODE_CHANOP
;
2731 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2732 change
->args
[1].mode
= MODE_CHANOP
;
2733 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2737 change
= mod_chanmode_alloc(1);
2739 change
->args
[0].mode
= MODE_CHANOP
;
2740 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2743 mod_chanmode_announce(chanserv
, channel
, change
);
2744 mod_chanmode_free(change
);
2747 static CHANSERV_FUNC(cmd_move
)
2749 struct mod_chanmode change
;
2750 struct chanNode
*target
;
2751 struct modeNode
*mn
;
2752 struct userData
*uData
;
2753 struct do_not_register
*dnr
;
2754 int chanserv_join
= 0, spamserv_join
;
2758 if(IsProtected(channel
->channel_info
))
2760 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2764 if(!IsChannelName(argv
[1]))
2766 reply("MSG_NOT_CHANNEL_NAME");
2770 if(opserv_bad_channel(argv
[1]))
2772 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2776 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2778 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2780 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2782 if(!IsHelping(user
))
2783 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2785 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2791 mod_chanmode_init(&change
);
2792 if(!(target
= GetChannel(argv
[1])))
2794 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2795 if(!IsSuspended(channel
->channel_info
))
2798 else if(target
->channel_info
)
2800 reply("CSMSG_ALREADY_REGGED", target
->name
);
2803 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2804 && !IsHelping(user
))
2806 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2809 else if(!IsSuspended(channel
->channel_info
))
2814 /* Clear MODE_REGISTERED from old channel, add it to new. */
2816 change
.modes_clear
= MODE_REGISTERED
;
2817 mod_chanmode_announce(chanserv
, channel
, &change
);
2818 change
.modes_clear
= 0;
2819 change
.modes_set
= MODE_REGISTERED
;
2820 mod_chanmode_announce(chanserv
, target
, &change
);
2823 /* Move the channel_info to the target channel; it
2824 shouldn't be necessary to clear timeq callbacks
2825 for the old channel. */
2826 target
->channel_info
= channel
->channel_info
;
2827 target
->channel_info
->channel
= target
;
2828 channel
->channel_info
= NULL
;
2830 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2833 ss_cs_join_channel(target
, spamserv_join
);
2835 if(!IsSuspended(target
->channel_info
))
2837 char reason2
[MAXLEN
];
2838 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2839 DelChannelUser(chanserv
, channel
, reason2
, 0);
2842 UnlockChannel(channel
);
2843 LockChannel(target
);
2844 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2845 channel
->name
, target
->name
, user
->handle_info
->handle
);
2847 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2852 merge_users(struct chanData
*source
, struct chanData
*target
)
2854 struct userData
*suData
, *tuData
, *next
;
2860 /* Insert the source's users into the scratch area. */
2861 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2862 dict_insert(merge
, suData
->handle
->handle
, suData
);
2864 /* Iterate through the target's users, looking for
2865 users common to both channels. The lower access is
2866 removed from either the scratch area or target user
2868 for(tuData
= target
->users
; tuData
; tuData
= next
)
2870 struct userData
*choice
;
2872 next
= tuData
->next
;
2874 /* If a source user exists with the same handle as a target
2875 channel's user, resolve the conflict by removing one. */
2876 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2880 /* Pick the data we want to keep. */
2881 /* If the access is the same, use the later seen time. */
2882 if(suData
->access
== tuData
->access
)
2883 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2884 else /* Otherwise, keep the higher access level. */
2885 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2887 /* Remove the user that wasn't picked. */
2888 if(choice
== tuData
)
2890 dict_remove(merge
, suData
->handle
->handle
);
2891 del_channel_user(suData
, 0);
2894 del_channel_user(tuData
, 0);
2897 /* Move the remaining users to the target channel. */
2898 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2900 suData
= iter_data(it
);
2902 /* Insert the user into the target channel's linked list. */
2903 suData
->prev
= NULL
;
2904 suData
->next
= target
->users
;
2905 suData
->channel
= target
;
2908 target
->users
->prev
= suData
;
2909 target
->users
= suData
;
2911 /* Update the user counts for the target channel; the
2912 source counts are left alone. */
2913 target
->userCount
++;
2916 /* Possible to assert (source->users == NULL) here. */
2917 source
->users
= NULL
;
2922 merge_bans(struct chanData
*source
, struct chanData
*target
)
2924 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2926 /* Hold on to the original head of the target ban list
2927 to avoid comparing source bans with source bans. */
2928 tFront
= target
->bans
;
2930 /* Perform a totally expensive O(n*m) merge, ick. */
2931 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2933 /* Flag to track whether the ban's been moved
2934 to the destination yet. */
2937 /* Possible to assert (sbData->prev == NULL) here. */
2938 sNext
= sbData
->next
;
2940 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2942 tNext
= tbData
->next
;
2944 /* Perform two comparisons between each source
2945 and target ban, conflicts are resolved by
2946 keeping the broader ban and copying the later
2947 expiration and triggered time. */
2948 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2950 /* There is a broader ban in the target channel that
2951 overrides one in the source channel; remove the
2952 source ban and break. */
2953 if(sbData
->expires
> tbData
->expires
)
2954 tbData
->expires
= sbData
->expires
;
2955 if(sbData
->triggered
> tbData
->triggered
)
2956 tbData
->triggered
= sbData
->triggered
;
2957 del_channel_ban(sbData
);
2960 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2962 /* There is a broader ban in the source channel that
2963 overrides one in the target channel; remove the
2964 target ban, fall through and move the source over. */
2965 if(tbData
->expires
> sbData
->expires
)
2966 sbData
->expires
= tbData
->expires
;
2967 if(tbData
->triggered
> sbData
->triggered
)
2968 sbData
->triggered
= tbData
->triggered
;
2969 if(tbData
== tFront
)
2971 del_channel_ban(tbData
);
2974 /* Source bans can override multiple target bans, so
2975 we allow a source to run through this loop multiple
2976 times, but we can only move it once. */
2981 /* Remove the source ban from the source ban list. */
2983 sbData
->next
->prev
= sbData
->prev
;
2985 /* Modify the source ban's associated channel. */
2986 sbData
->channel
= target
;
2988 /* Insert the ban into the target channel's linked list. */
2989 sbData
->prev
= NULL
;
2990 sbData
->next
= target
->bans
;
2993 target
->bans
->prev
= sbData
;
2994 target
->bans
= sbData
;
2996 /* Update the user counts for the target channel. */
3001 /* Possible to assert (source->bans == NULL) here. */
3002 source
->bans
= NULL
;
3006 merge_data(struct chanData
*source
, struct chanData
*target
)
3008 /* Use more recent visited and owner-transfer time; use older
3009 * registered time. Bitwise or may_opchan. Use higher max.
3010 * Do not touch last_refresh, ban count or user counts.
3012 if(source
->visited
> target
->visited
)
3013 target
->visited
= source
->visited
;
3014 if(source
->registered
< target
->registered
)
3015 target
->registered
= source
->registered
;
3016 if(source
->ownerTransfer
> target
->ownerTransfer
)
3017 target
->ownerTransfer
= source
->ownerTransfer
;
3018 if(source
->may_opchan
)
3019 target
->may_opchan
= 1;
3020 if(source
->max
> target
->max
)
3021 target
->max
= source
->max
;
3025 merge_channel(struct chanData
*source
, struct chanData
*target
)
3027 merge_users(source
, target
);
3028 merge_bans(source
, target
);
3029 merge_data(source
, target
);
3032 static CHANSERV_FUNC(cmd_merge
)
3034 struct userData
*target_user
;
3035 struct chanNode
*target
;
3036 char reason
[MAXLEN
];
3041 /* Make sure the target channel exists and is registered to the user
3042 performing the command. */
3043 if(!(target
= GetChannel(argv
[1])))
3045 reply("MSG_INVALID_CHANNEL");
3050 if (!irccasecmp("nodelete", argv
[2]))
3054 if(!target
->channel_info
)
3056 reply("CSMSG_NOT_REGISTERED", target
->name
);
3060 if(IsProtected(channel
->channel_info
))
3062 reply("CSMSG_MERGE_NODELETE");
3066 if(IsSuspended(target
->channel_info
))
3068 reply("CSMSG_MERGE_SUSPENDED");
3072 if(channel
== target
)
3074 reply("CSMSG_MERGE_SELF");
3078 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
3079 if(!target_user
|| (target_user
->access
< UL_OWNER
))
3081 reply("CSMSG_MERGE_NOT_OWNER");
3085 /* Merge the channel structures and associated data. */
3086 merge_channel(channel
->channel_info
, target
->channel_info
);
3087 spamserv_cs_move_merge(user
, channel
, target
, 0);
3088 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
3090 unregister_channel(channel
->channel_info
, reason
);
3091 reply("CSMSG_MERGE_SUCCESS", target
->name
);
3095 static CHANSERV_FUNC(cmd_opchan
)
3097 struct mod_chanmode change
;
3098 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
3100 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
3103 if(!IsInChannel(channel
,chanserv
)) {
3104 reply("CSMSG_NOT_IN_CHANNEL", channel
->name
);
3107 channel
->channel_info
->may_opchan
= 0;
3108 mod_chanmode_init(&change
);
3110 change
.args
[0].mode
= MODE_CHANOP
;
3111 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
3112 if(!change
.args
[0].u
.member
)
3114 reply("CSMSG_OUT_OF_CHANNEL", channel
->name
);
3117 mod_chanmode_announce(chanserv
, channel
, &change
);
3118 reply("CSMSG_OPCHAN_DONE", channel
->name
);
3122 static CHANSERV_FUNC(cmd_adduser
)
3124 struct userData
*actee
;
3125 struct userData
*actor
, *real_actor
;
3126 struct handle_info
*handle
= NULL
;
3127 //struct adduserPending *tmp;
3128 unsigned short access_level
, override
= 0;
3132 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
3134 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
3138 access_level
= user_level_from_name(argv
[2], UL_OWNER
);
3141 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3145 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3146 real_actor
= GetTrueChannelAccess(channel
->channel_info
, user
->handle_info
);
3148 if(actor
->access
<= access_level
)
3150 reply("CSMSG_NO_BUMP_ACCESS");
3154 /* Trying to add someone with equal/more access */
3155 if (!real_actor
|| real_actor
->access
<= access_level
)
3156 override
= CMD_LOG_OVERRIDE
;
3158 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3160 /* 'kevin must first authenticate with AuthServ'. is sent to user */
3161 struct userNode
*unode
;
3162 unode
= GetUserH(argv
[1]); /* find user struct by nick */
3165 if(find_adduser_pending(channel
, unode
)) {
3166 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
3169 if(IsInChannel(channel
, unode
)) {
3170 reply("CSMSG_ADDUSER_PENDING", unode
->nick
);
3171 add_adduser_pending(channel
, unode
, access_level
);
3172 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
3174 /* this results in user must auth AND not in chan errors. too confusing..
3176 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3184 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3186 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
3190 time_t accessexpiry
= 0;
3191 unsigned int duration
= 0;
3193 if ((duration
= ParseInterval(argv
[3])))
3194 accessexpiry
= now
+ duration
;
3197 actee
= add_channel_user(channel
->channel_info
, handle
, access_level
, 0, NULL
, accessexpiry
);
3198 scan_user_presence(actee
, NULL
);
3201 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
3203 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access_level
), access_level
);
3204 return 1 | override
;
3207 static CHANSERV_FUNC(cmd_clvl
)
3209 struct handle_info
*handle
;
3210 struct userData
*victim
;
3211 struct userData
*actor
, *real_actor
;
3212 unsigned short new_access
, override
= 0;
3213 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3217 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3218 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3220 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3223 if(handle
== user
->handle_info
&& !privileged
)
3225 reply("CSMSG_NO_SELF_CLVL");
3229 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3231 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3235 if(actor
->access
<= victim
->access
&& !privileged
)
3237 reply("MSG_USER_OUTRANKED", handle
->handle
);
3241 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
3245 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3249 if(new_access
>= actor
->access
&& !privileged
)
3251 reply("CSMSG_NO_BUMP_ACCESS");
3255 time_t clvlexpiry
= 0;
3256 unsigned int duration
= 0;
3258 if ((duration
= ParseInterval(argv
[3])))
3259 clvlexpiry
= now
+ duration
;
3263 if (victim
->accessexpiry
> 0) {
3264 reply("CSMSG_NO_BUMP_EXPIRY");
3268 victim
->clvlexpiry
= clvlexpiry
;
3269 victim
->lastaccess
= victim
->access
;
3270 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
3273 /* Trying to clvl a equal/higher user */
3274 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& handle
!= user
->handle_info
))
3275 override
= CMD_LOG_OVERRIDE
;
3276 /* Trying to clvl someone to equal/higher access */
3277 if(!real_actor
|| new_access
>= real_actor
->access
)
3278 override
= CMD_LOG_OVERRIDE
;
3279 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3280 * If they lower their own access it's not a big problem.
3282 victim
->access
= new_access
;
3283 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
3284 return 1 | override
;
3287 static CHANSERV_FUNC(cmd_deluser
)
3289 struct handle_info
*handle
;
3290 struct userData
*victim
;
3291 struct userData
*actor
, *real_actor
;
3292 unsigned short access_level
, override
= 0;
3293 unsigned short access_level_user
= 0;
3298 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3299 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3301 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
3304 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3306 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3312 access_level
= user_level_from_name(argv
[1], UL_OWNER
);
3313 char *useraccess
= user_level_name_from_level(victim
->access
);
3314 access_level_user
= user_level_from_name(useraccess
, UL_OWNER
);
3317 reply("CSMSG_INVALID_ACCESS", argv
[1]);
3320 if(access_level
!= access_level_user
)
3322 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, useraccess
, argv
[1]);
3328 access_level
= victim
->access
;
3331 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
3333 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
3337 /* If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override */
3338 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& real_actor
!= victim
))
3339 override
= CMD_LOG_OVERRIDE
;
3341 chan_name
= strdup(channel
->name
);
3342 del_channel_user(victim
, 1);
3343 reply("CSMSG_DELETED_USER", handle
->handle
, access_level
, chan_name
);
3345 return 1 | override
;
3349 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
3351 struct userData
*actor
, *real_actor
, *uData
, *next
;
3352 unsigned int override
= 0;
3354 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3355 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3357 if(min_access
> max_access
)
3359 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3363 if((actor
->access
<= max_access
) && !IsHelping(user
))
3365 reply("CSMSG_NO_ACCESS");
3369 if(!real_actor
|| real_actor
->access
<= max_access
)
3370 override
= CMD_LOG_OVERRIDE
;
3372 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3376 if((uData
->access
>= min_access
)
3377 && (uData
->access
<= max_access
)
3378 && match_ircglob(uData
->handle
->handle
, mask
))
3380 del_channel_user(uData
, 1);
3384 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3385 return 1 | override
;
3388 static CHANSERV_FUNC(cmd_mdelowner
)
3390 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3393 static CHANSERV_FUNC(cmd_mdelcoowner
)
3395 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3398 static CHANSERV_FUNC(cmd_mdelmanager
)
3400 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3403 static CHANSERV_FUNC(cmd_mdelop
)
3405 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3408 static CHANSERV_FUNC(cmd_mdelhalfop
)
3410 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3413 static CHANSERV_FUNC(cmd_mdelpeon
)
3415 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3418 static CHANSERV_FUNC(cmd_mdelpal
)
3420 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3423 static CHANSERV_FUNC(cmd_levels
)
3425 struct helpfile_table tbl
;
3428 tbl
.length
= 6 + 1; // 6 levels
3431 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3432 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3433 tbl
.contents
[0][0] = "Level";
3434 tbl
.contents
[0][1] = "From";
3435 tbl
.contents
[0][2] = "-";
3436 tbl
.contents
[0][3] = "To";
3438 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3439 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3440 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3441 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3442 tbl
.contents
[ii
][3] = msnprintf(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_COOWNER
));
3446 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3447 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3448 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-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_MANAGER
));
3452 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3453 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3454 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-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_OP
));
3458 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3459 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3460 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-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_HALFOP
));
3464 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3465 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3466 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3468 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3469 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3470 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3471 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3472 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3474 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3478 reply("CSMSG_LEVELS_HEADER");
3479 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3480 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3481 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3482 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3483 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3484 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3491 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3493 struct banData
*bData
, *next
;
3494 char interval
[INTERVALLEN
];
3499 limit
= now
- duration
;
3500 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3504 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3507 del_channel_ban(bData
);
3511 intervalString(interval
, duration
, user
->handle_info
);
3512 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3517 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
)
3519 struct userData
*actor
, *uData
, *next
;
3520 char interval
[INTERVALLEN
];
3524 actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3525 if(min_access
> max_access
)
3527 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3531 if(!actor
|| actor
->access
<= max_access
)
3533 reply("CSMSG_NO_ACCESS");
3538 limit
= now
- duration
;
3539 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3543 if((uData
->seen
> limit
)
3545 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3548 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3549 || (!max_access
&& (uData
->access
< actor
->access
)))
3551 del_channel_user(uData
, 1);
3559 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3561 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3565 static CHANSERV_FUNC(cmd_trim
)
3567 unsigned long duration
;
3568 unsigned short min_level
, max_level
;
3573 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3574 duration
= ParseInterval(argv
[2]);
3577 reply("CSMSG_CANNOT_TRIM");
3581 if(!irccasecmp(argv
[1], "lamers"))
3583 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3586 else if(!irccasecmp(argv
[1], "users"))
3588 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3591 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3593 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3596 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3598 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3603 reply("CSMSG_INVALID_TRIM", argv
[1]);
3608 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3609 to the user. cmd_all takes advantage of this. */
3610 static CHANSERV_FUNC(cmd_up
)
3612 struct mod_chanmode change
;
3613 struct userData
*uData
;
3616 mod_chanmode_init(&change
);
3618 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3619 if(!change
.args
[0].u
.member
)
3622 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3626 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3630 reply("CSMSG_GODMODE_UP", argv
[0]);
3633 else if(uData
->access
>= UL_OP
)
3635 change
.args
[0].mode
= MODE_CHANOP
;
3636 errmsg
= "CSMSG_ALREADY_OPPED";
3638 else if(uData
->access
>= UL_HALFOP
)
3640 change
.args
[0].mode
= MODE_HALFOP
;
3641 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3643 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3645 change
.args
[0].mode
= MODE_VOICE
;
3646 errmsg
= "CSMSG_ALREADY_VOICED";
3651 reply("CSMSG_NO_ACCESS");
3654 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3655 if(!change
.args
[0].mode
)
3658 reply(errmsg
, channel
->name
);
3661 modcmd_chanmode_announce(&change
);
3665 static CHANSERV_FUNC(cmd_down
)
3667 struct mod_chanmode change
;
3669 mod_chanmode_init(&change
);
3671 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3672 if(!change
.args
[0].u
.member
)
3675 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3679 if(!change
.args
[0].u
.member
->modes
)
3682 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3686 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3687 modcmd_chanmode_announce(&change
);
3691 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
)
3693 struct userData
*cList
;
3695 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3697 if(IsSuspended(cList
->channel
)
3698 || IsUserSuspended(cList
)
3699 || !GetUserMode(cList
->channel
->channel
, user
))
3702 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3708 static CHANSERV_FUNC(cmd_upall
)
3710 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3713 static CHANSERV_FUNC(cmd_downall
)
3715 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3718 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3719 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3722 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
)
3724 unsigned int ii
, valid
;
3725 struct userNode
*victim
;
3726 struct mod_chanmode
*change
;
3728 change
= mod_chanmode_alloc(argc
- 1);
3730 for(ii
=valid
=0; ++ii
< argc
; )
3732 if(!(victim
= GetUserH(argv
[ii
])))
3734 change
->args
[valid
].mode
= mode
;
3735 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3736 if(!change
->args
[valid
].u
.member
)
3738 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3743 change
->argc
= valid
;
3744 if(valid
< (argc
-1))
3745 reply("CSMSG_PROCESS_FAILED");
3748 modcmd_chanmode_announce(change
);
3749 reply(action
, channel
->name
);
3751 mod_chanmode_free(change
);
3755 static CHANSERV_FUNC(cmd_op
)
3757 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3760 static CHANSERV_FUNC(cmd_hop
)
3762 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3765 static CHANSERV_FUNC(cmd_deop
)
3767 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3770 static CHANSERV_FUNC(cmd_dehop
)
3772 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3775 static CHANSERV_FUNC(cmd_voice
)
3777 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3780 static CHANSERV_FUNC(cmd_devoice
)
3782 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3786 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3793 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3795 struct modeNode
*mn
= channel
->members
.list
[ii
];
3797 if(IsService(mn
->user
))
3800 b
= user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
3806 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3810 victims
[(*victimCount
)++] = mn
;
3815 int is_extban(char *b
) {
3823 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3825 struct userNode
*victim
;
3826 struct modeNode
**victims
;
3827 unsigned int offset
, n
, victimCount
, duration
= 0;
3829 char *reason
= "Bye.", *ban
, *name
;
3830 char interval
[INTERVALLEN
];
3832 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3833 REQUIRE_PARAMS(offset
);
3836 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3837 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3839 /* Truncate the reason to a length of TOPICLEN, as
3840 the ircd does; however, leave room for an ellipsis
3841 and the kicker's nick. */
3842 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3846 if((victim
= GetUserH(argv
[1])))
3848 victims
= alloca(sizeof(victims
[0]));
3849 victims
[0] = GetUserMode(channel
, victim
);
3850 /* XXX: The comparison with ACTION_KICK is just because all
3851 * other actions can work on users outside the channel, and we
3852 * want to allow those (e.g. unbans) in that case. If we add
3853 * some other ejection action for in-channel users, change
3855 victimCount
= victims
[0] ? 1 : 0;
3857 if(IsService(victim
))
3860 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3864 if((action
== ACTION_KICK
) && !victimCount
)
3867 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3871 if(protect_user(victim
, user
, channel
->channel_info
, false))
3873 // This translates to send_message(user, cmd->parent->bot, ...)
3874 // if user is x3 (ctcp action) cmd is null and segfault.
3876 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3880 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3881 name
= victim
->nick
;
3883 else if(!is_ircmask(argv
[1]) && (*argv
[1] == '*'))
3885 struct handle_info
*hi
;
3886 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
3887 const char *accountname
= argv
[1] + 1;
3889 if(!(hi
= get_handle_info(accountname
)))
3891 reply("MSG_HANDLE_UNKNOWN", accountname
);
3895 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", hi
->handle
);
3896 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3898 b
= bad_channel_ban(channel
, user
, banmask
, &victimCount
, victims
);
3901 reply("CSMSG_MASK_PROTECTED", banmask
);
3906 reply("CSMSG_BAD_BAN", banmask
);
3910 if((action
== ACTION_KICK
) && (victimCount
== 0))
3912 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, banmask
);
3916 name
= ban
= strdup(banmask
);
3920 if(!is_ircmask(argv
[1]))
3923 reply("MSG_NICK_UNKNOWN", argv
[1]);
3927 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3929 b
= bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
);
3930 if(cmd
&& (b
== 1)) {
3931 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3934 else if(cmd
&& (b
== -1)) {
3935 reply("CSMSG_BAD_BAN", argv
[1]);
3938 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3939 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3941 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3942 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3943 some creativity, but its not x3's job to be the ban censor anyway. */
3944 if(is_overmask(argv
[1]))
3947 reply("CSMSG_LAME_MASK", argv
[1]);
3950 //TODO: We have no support to do protection etc etc so for now we dont let you use x3 to set extended bans.
3951 if(is_extban(argv
[1]))
3954 reply("CSMSG_NO_EXTBANS", argv
[1]);
3958 if((action
== ACTION_KICK
) && (victimCount
== 0))
3961 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3965 name
= ban
= strdup(argv
[1]);
3968 /* Truncate the ban in place if necessary; we must ensure
3969 that 'ban' is a valid ban mask before sanitizing it. */
3971 sanitize_ircmask(ban
);
3973 if(action
& ACTION_ADD_LAMER
)
3975 struct banData
*bData
, *next
;
3977 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3980 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3985 if(action
& ACTION_ADD_TIMED_LAMER
)
3987 duration
= ParseInterval(argv
[2]);
3992 reply("CSMSG_DURATION_TOO_LOW");
3996 else if(duration
> (86400 * 365 * 2))
3999 reply("CSMSG_DURATION_TOO_HIGH");
4006 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
4008 if(match_ircglobs(bData
->mask
, ban
))
4010 int exact
= !irccasecmp(bData
->mask
, ban
);
4012 /* The ban is redundant; there is already a ban
4013 with the same effect in place. */
4017 free(bData
->reason
);
4018 bData
->reason
= strdup(reason
);
4019 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
4021 reply("CSMSG_REASON_CHANGE", ban
);
4025 if(exact
&& bData
->expires
)
4029 /* If the ban matches an existing one exactly,
4030 extend the expiration time if the provided
4031 duration is longer. */
4032 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
4034 bData
->expires
= now
+ duration
;
4045 /* Delete the expiration timeq entry and
4046 requeue if necessary. */
4047 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
4050 timeq_add(bData
->expires
, expire_ban
, bData
);
4054 /* automated kickban, dont reply */
4057 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
4059 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4065 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
4072 if(match_ircglobs(ban
, bData
->mask
))
4074 /* The ban we are adding makes previously existing
4075 bans redundant; silently remove them. */
4076 del_channel_ban(bData
);
4080 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
);
4082 name
= ban
= strdup(bData
->mask
);
4086 /* WHAT DOES THIS DO?? -Rubin */
4087 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
4089 extern const char *hidden_host_suffix
;
4090 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
4092 unsigned int l1
, l2
;
4095 l2
= strlen(old_name
);
4098 if(irccasecmp(ban
+ l1
- l2
, old_name
))
4100 new_mask
= malloc(MAXLEN
);
4101 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
4103 name
= ban
= new_mask
;
4108 if(action
& ACTION_BAN
)
4110 unsigned int exists
;
4111 struct mod_chanmode
*change
;
4113 if(channel
->banlist
.used
>= MAXBANS
)
4116 reply("CSMSG_BANLIST_FULL", channel
->name
);
4121 exists
= ChannelBanExists(channel
, ban
);
4122 change
= mod_chanmode_alloc(victimCount
+ 1);
4123 for(n
= 0; n
< victimCount
; ++n
)
4125 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
4126 change
->args
[n
].u
.member
= victims
[n
];
4130 change
->args
[n
].mode
= MODE_BAN
;
4131 change
->args
[n
++].u
.hostmask
= ban
;
4135 modcmd_chanmode_announce(change
);
4137 mod_chanmode_announce(chanserv
, channel
, change
);
4138 mod_chanmode_free(change
);
4140 if(exists
&& (action
== ACTION_BAN
))
4143 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
4149 if(action
& ACTION_ADD_LAMER
)
4151 char kick_reason
[MAXLEN
];
4152 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4154 for(n
= 0; n
< victimCount
; n
++)
4156 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true))
4158 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4162 else if(action
& ACTION_KICK
)
4164 char kick_reason
[MAXLEN
];
4165 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4167 for(n
= 0; n
< victimCount
; n
++)
4169 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4175 /* No response, since it was automated. */
4177 else if(action
& ACTION_ADD_LAMER
)
4180 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
4182 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4184 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
4185 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
4186 else if(action
& ACTION_BAN
)
4187 reply("CSMSG_BAN_DONE", name
, channel
->name
);
4188 else if(action
& ACTION_KICK
&& victimCount
)
4189 reply("CSMSG_KICK_DONE", name
, channel
->name
);
4195 static CHANSERV_FUNC(cmd_kickban
)
4197 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
4200 static CHANSERV_FUNC(cmd_kick
)
4202 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
4205 static CHANSERV_FUNC(cmd_ban
)
4207 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
4210 static CHANSERV_FUNC(cmd_addlamer
)
4212 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
4215 static CHANSERV_FUNC(cmd_addtimedlamer
)
4217 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
4220 static struct mod_chanmode
*
4221 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
4223 struct mod_chanmode
*change
;
4224 unsigned char *match
;
4225 unsigned int ii
, count
;
4227 match
= alloca(bans
->used
);
4230 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4232 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
4233 MATCH_USENICK
| MATCH_VISIBLE
, 0);
4240 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4242 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
4249 change
= mod_chanmode_alloc(count
);
4250 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4254 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
4255 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
4257 assert(count
== change
->argc
);
4261 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
4263 unsigned int jj
, ii
, count
;
4265 struct chanData
*channel
;
4267 struct mod_chanmode
*change
;
4269 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
4270 /* Walk through every channel */
4271 for(channel
= channelList
; channel
; channel
= channel
->next
) {
4272 switch(channel
->chOpts
[chBanTimeout
])
4274 default: case '0': continue; /* Dont remove bans in this chan */
4275 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
4276 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
4277 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
4278 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
4279 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
4282 /* First find out how many bans were going to unset */
4283 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4284 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see below
4285 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
&& !is_extban(channel
->channel
->banlist
.list
[jj
]->ban
))
4289 /* At least one ban, so setup a removal */
4290 change
= mod_chanmode_alloc(count
);
4292 /* Walk over every ban in this channel.. */
4293 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4294 bn
= channel
->channel
->banlist
.list
[jj
];
4295 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see above
4296 if (bn
->set
< bantimeout
&& !is_extban(bn
->ban
)) {
4297 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
4299 /* Add this ban to the mode change */
4300 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4301 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
4303 /* Pull this ban out of the list */
4304 banList_remove(&(channel
->channel
->banlist
), bn
);
4309 /* Send the modes to IRC */
4310 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
4312 /* free memory from strdup above */
4313 for(ii
= 0; ii
< count
; ++ii
)
4314 free((char*)change
->args
[ii
].u
.hostmask
);
4316 mod_chanmode_free(change
);
4319 /* Set this function to run again */
4320 if(chanserv_conf
.ban_timeout_frequency
)
4321 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
4326 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
4328 struct userNode
*actee
;
4334 /* may want to allow a comma delimited list of users... */
4335 if(!(actee
= GetUserH(argv
[1])))
4337 if(!is_ircmask(argv
[1]) && *argv
[1] == '*')
4339 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
4340 const char *accountname
= argv
[1] + 1;
4342 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", accountname
);
4343 mask
= strdup(banmask
);
4345 else if(!is_ircmask(argv
[1]))
4347 reply("MSG_NICK_UNKNOWN", argv
[1]);
4352 mask
= strdup(argv
[1]);
4356 /* We don't sanitize the mask here because ircu
4358 if(action
& ACTION_UNBAN
)
4360 struct mod_chanmode
*change
;
4361 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
4366 modcmd_chanmode_announce(change
);
4367 for(ii
= 0; ii
< change
->argc
; ++ii
)
4368 free((char*)change
->args
[ii
].u
.hostmask
);
4369 mod_chanmode_free(change
);
4374 if(action
& ACTION_DEL_LAMER
)
4376 struct banData
*ban
, *next
;
4378 ban
= channel
->channel_info
->bans
; /* lamers */
4382 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0);
4385 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
4390 del_channel_ban(ban
);
4397 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
4399 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
4405 static CHANSERV_FUNC(cmd_unban
)
4407 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
4410 static CHANSERV_FUNC(cmd_dellamer
)
4412 /* it doesn't necessarily have to remove the channel ban - may want
4413 to make that an option. */
4414 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
4417 static CHANSERV_FUNC(cmd_unbanme
)
4419 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4420 long flags
= ACTION_UNBAN
;
4422 /* remove permanent bans if the user has the proper access. */
4423 if(uData
->access
>= UL_MANAGER
)
4424 flags
|= ACTION_DEL_LAMER
;
4426 argv
[1] = user
->nick
;
4427 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
4430 static CHANSERV_FUNC(cmd_unbanall
)
4432 struct mod_chanmode
*change
;
4435 if(!channel
->banlist
.used
)
4437 reply("CSMSG_NO_BANS", channel
->name
);
4441 // TODO: dont remove some kinds of extended bans such as ~c
4442 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4443 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4445 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4446 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4448 modcmd_chanmode_announce(change
);
4449 for(ii
= 0; ii
< change
->argc
; ++ii
)
4450 free((char*)change
->args
[ii
].u
.hostmask
);
4451 mod_chanmode_free(change
);
4452 reply("CSMSG_BANS_REMOVED", channel
->name
);
4456 static CHANSERV_FUNC(cmd_open
)
4458 struct mod_chanmode
*change
;
4461 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4463 change
= mod_chanmode_alloc(0);
4464 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4465 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4466 && channel
->channel_info
->modes
.modes_set
)
4467 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4468 modcmd_chanmode_announce(change
);
4469 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4470 for(ii
= 0; ii
< change
->argc
; ++ii
)
4471 free((char*)change
->args
[ii
].u
.hostmask
);
4472 mod_chanmode_free(change
);
4476 static CHANSERV_FUNC(cmd_myaccess
)
4478 static struct string_buffer sbuf
;
4479 struct handle_info
*target_handle
;
4480 struct userData
*uData
;
4483 target_handle
= user
->handle_info
;
4484 else if(!IsStaff(user
))
4486 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4489 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4492 if(!oper_outranks(user
, target_handle
))
4495 if(!target_handle
->channels
)
4497 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4501 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4502 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4504 struct chanData
*cData
= uData
->channel
;
4506 if(uData
->access
> UL_OWNER
)
4508 if(IsProtected(cData
)
4509 && (target_handle
!= user
->handle_info
)
4510 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4513 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4514 if(uData
->flags
!= 0)
4515 string_buffer_append(&sbuf
, ',');
4516 if(IsUserSuspended(uData
))
4517 string_buffer_append(&sbuf
, 's');
4518 if(IsUserAutoOp(uData
))
4520 if(uData
->access
>= UL_OP
)
4521 string_buffer_append(&sbuf
, 'o');
4522 else if(uData
->access
>= UL_HALFOP
)
4523 string_buffer_append(&sbuf
, 'h');
4524 else if(uData
->access
>= UL_PEON
)
4525 string_buffer_append(&sbuf
, 'v');
4527 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4528 string_buffer_append(&sbuf
, 'i');
4529 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4530 string_buffer_append(&sbuf
, 'j');
4532 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4534 string_buffer_append_string(&sbuf
, ")]");
4535 string_buffer_append(&sbuf
, '\0');
4536 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4542 static CHANSERV_FUNC(cmd_access
)
4544 struct userNode
*target
;
4545 struct handle_info
*target_handle
;
4546 struct userData
*uData
;
4548 char prefix
[MAXLEN
];
4553 target_handle
= target
->handle_info
;
4555 else if((target
= GetUserH(argv
[1])))
4557 target_handle
= target
->handle_info
;
4559 else if(argv
[1][0] == '*')
4561 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4563 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4569 reply("MSG_NICK_UNKNOWN", argv
[1]);
4573 assert(target
|| target_handle
);
4575 if(target
== chanserv
)
4577 reply("CSMSG_IS_CHANSERV");
4585 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4590 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4593 reply("MSG_AUTHENTICATE");
4599 const char *epithet
= NULL
, *type
= NULL
;
4602 epithet
= chanserv_conf
.irc_operator_epithet
;
4603 type
= user_find_message(user
, "CSMSG_OPERATOR_TITLE");
4605 else if(IsNetworkHelper(target
))
4607 epithet
= chanserv_conf
.network_helper_epithet
;
4608 type
= user_find_message(user
, "CSMSG_UC_H_TITLE");
4610 else if(IsSupportHelper(target
))
4612 epithet
= chanserv_conf
.support_helper_epithet
;
4613 type
= user_find_message(user
, "CSMSG_LC_H_TITLE");
4617 if(target_handle
->epithet
)
4618 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4620 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4622 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4626 sprintf(prefix
, "%s", target_handle
->handle
);
4629 if(!channel
->channel_info
)
4631 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4635 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4636 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4637 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4639 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4640 /* To prevent possible information leaks, only show infolines
4641 * if the requestor is in the channel or it's their own
4643 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4645 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4647 /* Likewise, only say it's suspended if the user has active
4648 * access in that channel or it's their own entry. */
4649 if(IsUserSuspended(uData
)
4650 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4651 || (user
->handle_info
== uData
->handle
)))
4653 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4658 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4664 /* This is never used...
4666 zoot_list(struct listData *list)
4668 struct userData *uData;
4669 unsigned int start, curr, highest, lowest;
4670 struct helpfile_table tmp_table;
4671 const char **temp, *msg;
4673 if(list->table.length == 1)
4676 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);
4678 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));
4679 msg = user_find_message(list->user, "MSG_NONE");
4680 send_message_type(4, list->user, list->bot, " %s", msg);
4682 tmp_table.width = list->table.width;
4683 tmp_table.flags = list->table.flags;
4684 list->table.contents[0][0] = " ";
4685 highest = list->highest;
4686 if(list->lowest != 0)
4687 lowest = list->lowest;
4688 else if(highest < 100)
4691 lowest = highest - 100;
4692 for(start = curr = 1; curr < list->table.length; )
4694 uData = list->users[curr-1];
4695 list->table.contents[curr++][0] = " ";
4696 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4699 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);
4701 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));
4702 temp = list->table.contents[--start];
4703 list->table.contents[start] = list->table.contents[0];
4704 tmp_table.contents = list->table.contents + start;
4705 tmp_table.length = curr - start;
4706 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4707 list->table.contents[start] = temp;
4709 highest = lowest - 1;
4710 lowest = (highest < 100) ? 0 : (highest - 99);
4717 normal_list(struct listData
*list
)
4721 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
);
4723 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
));
4724 if(list
->table
.length
== 1)
4726 msg
= user_find_message(list
->user
, "MSG_NONE");
4727 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4730 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4733 /* if these need changed, uncomment and customize
4735 clean_list(struct listData *list)
4739 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);
4741 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));
4742 if(list->table.length == 1)
4744 msg = user_find_message(list->user, "MSG_NONE");
4745 send_message_type(4, list->user, list->bot, " %s", msg);
4748 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4752 advanced_list(struct listData *list)
4756 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);
4758 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));
4759 if(list->table.length == 1)
4761 msg = user_find_message(list->user, "MSG_NONE");
4762 send_message_type(4, list->user, list->bot, " %s", msg);
4765 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4769 classic_list(struct listData *list)
4773 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4775 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4776 if(list->table.length == 1)
4778 msg = user_find_message(list->user, "MSG_NONE");
4779 send_message_type(4, list->user, list->bot, " %s", msg);
4782 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4787 userData_access_comp(const void *arg_a
, const void *arg_b
)
4789 const struct userData
*a
= *(struct userData
**)arg_a
;
4790 const struct userData
*b
= *(struct userData
**)arg_b
;
4792 if(a
->access
!= b
->access
)
4793 res
= b
->access
- a
->access
;
4795 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4800 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4802 void (*send_list
)(struct listData
*);
4803 struct userData
*uData
;
4804 struct listData lData
;
4805 unsigned int matches
;
4811 lData
.bot
= cmd
->parent
->bot
;
4812 lData
.channel
= channel
;
4813 lData
.lowest
= lowest
;
4814 lData
.highest
= highest
;
4815 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4816 send_list
= normal_list
;
4817 /* What does the following line do exactly?? */
4818 /*(void)zoot_list; ** since it doesn't show user levels */
4821 if(user->handle_info)
4823 switch(user->handle_info->userlist_style)
4825 case HI_STYLE_CLEAN:
4826 send_list = clean_list;
4828 case HI_STYLE_ADVANCED:
4829 send_list = advanced_list;
4831 case HI_STYLE_CLASSIC:
4832 send_list = classic_list;
4834 case HI_STYLE_NORMAL:
4836 send_list = normal_list;
4841 send_list
= normal_list
;
4843 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4845 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4847 if((uData
->access
< lowest
)
4848 || (uData
->access
> highest
)
4849 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4851 lData
.users
[matches
++] = uData
;
4853 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4855 lData
.table
.length
= matches
+1;
4856 lData
.table
.flags
= TABLE_NO_FREE
;
4857 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4859 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4860 lData
.table
.width
= 6; /* with level = 6 */
4862 lData
.table
.width
= 5; /* without = 5 */
4863 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4864 lData
.table
.contents
[0] = ary
;
4865 if(user
->handle_info
) {
4866 switch(user
->handle_info
->userlist_style
) {
4867 case HI_STYLE_CLASSIC
:
4870 case HI_STYLE_ADVANCED
:
4871 ary
[i
++] = "Access";
4874 case HI_STYLE_CLEAN
:
4875 ary
[i
++] = "Access";
4877 case HI_STYLE_NORMAL
:
4879 ary
[i
++] = "Access";
4884 ary
[i
++] = "Access";
4886 ary
[i
++] = "Account";
4887 ary
[i
] = "Last Seen";
4889 ary
[i
++] = "Status";
4890 ary
[i
++] = "Expiry";
4891 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4893 char seen
[INTERVALLEN
];
4896 uData
= lData
.users
[matches
-1];
4898 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4899 lData
.table
.contents
[matches
] = ary
;
4900 if(user
->handle_info
) {
4901 switch(user
->handle_info
->userlist_style
) {
4902 case HI_STYLE_CLASSIC
:
4903 ary
[i
++] = strtab(uData
->access
);
4905 case HI_STYLE_ADVANCED
:
4906 ary
[i
++] = user_level_name_from_level(uData
->access
);
4907 ary
[i
++] = strtab(uData
->access
);
4909 case HI_STYLE_CLEAN
:
4910 ary
[i
++] = user_level_name_from_level(uData
->access
);
4912 case HI_STYLE_NORMAL
:
4914 ary
[i
++] = user_level_name_from_level(uData
->access
);
4919 ary
[i
++] = user_level_name_from_level(uData
->access
);
4921 ary
[i
++] = uData
->handle
->handle
;
4924 else if(!uData
->seen
)
4927 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4928 ary
[i
] = strdup(ary
[i
]);
4930 if(IsUserSuspended(uData
))
4931 ary
[i
++] = "Suspended";
4932 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4933 ary
[i
++] = "Vacation";
4935 ary
[i
++] = "Normal";
4937 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4938 char delay
[INTERVALLEN
];
4941 if (uData
->accessexpiry
> 0) {
4942 diff
= uData
->accessexpiry
- now
;
4943 intervalString(delay
, diff
, user
->handle_info
);
4945 diff
= uData
->clvlexpiry
- now
;
4946 intervalString(delay
, diff
, user
->handle_info
);
4955 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4957 /* Free strdup above */
4958 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4959 free(lData
.table
.contents
[matches
]);
4961 free(lData
.table
.contents
[0]);
4962 free(lData
.table
.contents
);
4966 /* Remove this now that debugging is over? or improve it for
4967 * users? Would it be better tied into USERS somehow? -Rubin */
4968 static CHANSERV_FUNC(cmd_pending
)
4970 struct adduserPending
*ap
;
4971 reply("CSMSG_ADDUSER_PENDING_HEADER");
4972 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4974 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4975 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4976 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4980 static CHANSERV_FUNC(cmd_users
)
4982 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4985 static CHANSERV_FUNC(cmd_wlist
)
4987 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4990 static CHANSERV_FUNC(cmd_clist
)
4992 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4995 static CHANSERV_FUNC(cmd_mlist
)
4997 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
5000 static CHANSERV_FUNC(cmd_olist
)
5002 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
5005 static CHANSERV_FUNC(cmd_hlist
)
5007 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
5010 static CHANSERV_FUNC(cmd_plist
)
5012 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
5015 static CHANSERV_FUNC(cmd_lamers
)
5017 struct userNode
*search_u
= NULL
;
5018 struct helpfile_table tbl
;
5019 unsigned int matches
= 0, timed
= 0, search_wilds
= 0, ii
;
5020 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
5021 const char *msg_never
, *triggered
, *expires
;
5022 struct banData
*ban
, **bans
; /* lamers */
5026 else if(strchr(search
= argv
[1], '!'))
5029 search_wilds
= search
[strcspn(search
, "?*")];
5031 else if(!(search_u
= GetUserH(search
)))
5032 reply("MSG_NICK_UNKNOWN", search
);
5034 reply("CSMSG_LAMERS_HEADER", channel
->name
);
5035 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
5038 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
5042 if(!user_matches_glob(search_u
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
5047 if(search_wilds
? !match_ircglobs(search
, ban
->mask
) : !match_ircglob(search
, ban
->mask
))
5050 bans
[matches
++] = ban
;
5055 tbl
.length
= matches
+ 1;
5056 tbl
.width
= 4 + timed
;
5058 tbl
.flags
= TABLE_NO_FREE
;
5059 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
5060 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5061 tbl
.contents
[0][0] = "Mask";
5062 tbl
.contents
[0][1] = "Set By";
5063 tbl
.contents
[0][2] = "Triggered";
5066 tbl
.contents
[0][3] = "Expires";
5067 tbl
.contents
[0][4] = "Reason";
5070 tbl
.contents
[0][3] = "Reason";
5073 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5074 /* reply("MSG_NONE"); */
5075 free(tbl
.contents
[0]);
5080 msg_never
= user_find_message(user
, "MSG_NEVER");
5081 for(ii
= 0; ii
< matches
; )
5087 else if(ban
->expires
)
5088 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
5090 expires
= msg_never
;
5093 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
5095 triggered
= msg_never
;
5097 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
5098 tbl
.contents
[ii
][0] = ban
->mask
;
5099 tbl
.contents
[ii
][1] = ban
->owner
;
5100 tbl
.contents
[ii
][2] = strdup(triggered
);
5103 tbl
.contents
[ii
][3] = strdup(expires
);
5104 tbl
.contents
[ii
][4] = ban
->reason
;
5107 tbl
.contents
[ii
][3] = ban
->reason
;
5109 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
5110 /* reply("MSG_MATCH_COUNT", matches); */
5111 for(ii
= 1; ii
< tbl
.length
; ++ii
)
5113 free((char*)tbl
.contents
[ii
][2]);
5115 free((char*)tbl
.contents
[ii
][3]);
5116 free(tbl
.contents
[ii
]);
5118 free(tbl
.contents
[0]);
5125 * return + if the user does NOT have the right to set the topic, and
5126 * the topic is changed.
5129 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
5131 struct chanData
*cData
= channel
->channel_info
;
5132 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5134 else if(cData
->topic
)
5135 return irccasecmp(new_topic
, cData
->topic
);
5142 * Makes a givin topic fit into a givin topic mask and returns
5145 * topic_mask - the mask to conform to
5146 * topic - the topic to make conform
5147 * new_topic - the pre-allocated char* to put the new topic into
5149 * modifies: new_topic
5152 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
5154 //char *topic_mask = cData->topic_mask;
5156 int pos
=0, starpos
=-1, dpos
=0, len
;
5158 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
5165 strcpy(new_topic
, "");
5168 len
= strlen(topic
);
5169 if((dpos
+ len
) > TOPICLEN
)
5170 len
= TOPICLEN
+ 1 - dpos
;
5171 memcpy(new_topic
+dpos
, topic
, len
);
5175 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
5176 default: new_topic
[dpos
++] = tchar
; break;
5179 if((dpos
> TOPICLEN
) || tchar
)
5181 strcpy(new_topic
, "");
5184 new_topic
[dpos
] = 0;
5188 static CHANSERV_FUNC(cmd_topic
)
5190 struct chanData
*cData
;
5194 #ifdef WITH_PROTOCOL_P10
5198 cData
= channel
->channel_info
;
5203 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5204 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
5205 reply("CSMSG_TOPIC_SET", cData
->topic
);
5209 reply("CSMSG_NO_TOPIC", channel
->name
);
5213 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5214 /* If they say "!topic *", use an empty topic. */
5215 if((topic
[0] == '*') && (topic
[1] == 0))
5218 if(bad_topic(channel
, user
, topic
))
5220 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5225 /* If there is a topicmask set, and the new topic doesnt match, make it */
5226 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
5228 char *topic_mask
= cData
->topic_mask
;
5229 char new_topic
[TOPICLEN
+1];
5231 /* make a new topic fitting mask */
5232 conform_topic(topic_mask
, topic
, new_topic
);
5235 /* Topic couldnt fit into mask, was too long */
5236 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
5237 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
5240 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
5242 else /* No mask set, just set the topic */
5243 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
5246 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
5248 /* Grab the topic and save it as the default topic. */
5250 cData
->topic
= strdup(channel
->topic
);
5256 static CHANSERV_FUNC(cmd_mode
)
5258 struct userData
*uData
;
5259 struct mod_chanmode
*change
;
5264 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
))
5266 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5270 change
= &channel
->channel_info
->modes
;
5271 if(change
->modes_set
|| change
->modes_clear
) {
5272 modcmd_chanmode_announce(change
);
5273 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
5276 reply("CSMSG_NO_MODES", channel
->name
);
5280 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5282 base_oplevel
= MAXOPLEVEL
;
5283 else if (uData
->access
>= UL_OWNER
)
5286 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
5287 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
5291 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
5295 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
5296 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
5299 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5300 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
5304 modcmd_chanmode_announce(change
);
5305 mod_chanmode_free(change
);
5306 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
5310 static CHANSERV_FUNC(cmd_invite
)
5312 //struct userData *uData;
5313 struct userNode
*invite
;
5315 //uData = GetChannelUser(channel->channel_info, user->handle_info);
5319 if(!(invite
= GetUserH(argv
[1])))
5321 reply("MSG_NICK_UNKNOWN", argv
[1]);
5328 if(GetUserMode(channel
, invite
))
5330 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
5338 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5339 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
5342 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
5345 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
5347 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++)
5349 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
, 0))
5351 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
5357 irc_invite(chanserv
, invite
, channel
);
5359 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
5364 static CHANSERV_FUNC(cmd_inviteme
)
5366 if(GetUserMode(channel
, user
))
5368 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
5371 if(channel
->channel_info
5372 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
5374 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
5377 irc_invite(cmd
->parent
->bot
, user
, channel
);
5382 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
5385 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
5387 /* We display things based on two dimensions:
5388 * - Issue time: present or absent
5389 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5390 * (in order of precedence, so something both expired and revoked
5391 * only counts as revoked)
5393 combo
= (suspended
->issued
? 4 : 0)
5394 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
5396 case 0: /* no issue time, indefinite expiration */
5397 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
5399 case 1: /* no issue time, expires in future */
5400 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
5401 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
5403 case 2: /* no issue time, expired */
5404 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
5405 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
5407 case 3: /* no issue time, revoked */
5408 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
5409 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
5411 case 4: /* issue time set, indefinite expiration */
5412 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5413 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
5415 case 5: /* issue time set, expires in future */
5416 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5417 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
5418 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5420 case 6: /* issue time set, expired */
5421 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5422 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
5423 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5425 case 7: /* issue time set, revoked */
5426 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5427 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
5428 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5431 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
5437 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
5440 const char *fmt
= "%a %b %d %H:%M %Y";
5441 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
5443 if(giveownership
->staff_issuer
)
5445 if(giveownership
->reason
)
5446 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
5447 giveownership
->target
, giveownership
->target_access
,
5448 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
5450 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
5451 giveownership
->target
, giveownership
->target_access
,
5452 giveownership
->staff_issuer
, buf
);
5456 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5461 static CHANSERV_FUNC(cmd_info
)
5463 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5464 struct userData
*uData
, *owner
;
5465 struct chanData
*cData
;
5466 struct do_not_register
*dnr
;
5471 cData
= channel
->channel_info
;
5472 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5473 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5476 uData
= GetChannelUser(cData
, user
->handle_info
);
5477 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5479 mod_chanmode_format(&cData
->modes
, modes
);
5480 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5481 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5484 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5488 note
= iter_data(it
);
5489 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5492 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5493 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5496 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5497 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5498 if(owner
->access
== UL_OWNER
)
5499 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5500 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5501 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5502 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5504 privileged
= IsStaff(user
);
5505 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5506 if(cData
->registrar
)
5507 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5509 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5510 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5512 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5514 struct suspended
*suspended
;
5515 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5516 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5517 show_suspension_info(cmd
, user
, suspended
);
5519 else if(IsSuspended(cData
))
5521 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5522 show_suspension_info(cmd
, user
, cData
->suspended
);
5524 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5526 struct giveownership
*giveownership
;
5527 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5528 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5529 show_giveownership_info(cmd
, user
, giveownership
);
5531 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5532 reply("CSMSG_CHANNEL_END");
5534 reply("CSMSG_CHANNEL_END_CLEAN");
5538 static CHANSERV_FUNC(cmd_netinfo
)
5540 extern time_t boot_time
;
5541 extern unsigned long burst_length
;
5542 char interval
[INTERVALLEN
];
5544 reply("CSMSG_NETWORK_INFO");
5545 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5546 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5547 reply("CSMSG_NETWORK_OPERS", count_opers
);
5548 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5549 reply("CSMSG_NETWORK_LAMERS", banCount
);
5550 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5551 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5552 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5557 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5559 struct helpfile_table table
;
5561 struct userNode
*user
;
5566 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5567 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5568 for(nn
=0; nn
<list
->used
; nn
++)
5570 user
= list
->list
[nn
];
5571 if(user
->modes
& skip_flags
)
5573 if(IsBot(user
) || IsHideOper(user
))
5575 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5578 nick
= alloca(strlen(user
->nick
)+3);
5579 sprintf(nick
, "(%s)", user
->nick
);
5583 table
.contents
[table
.length
][0] = nick
;
5586 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5589 static CHANSERV_FUNC(cmd_ircops
)
5591 reply("CSMSG_STAFF_OPERS");
5592 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
| FLAGS_BOT
);
5596 static CHANSERV_FUNC(cmd_helpers
)
5598 reply("CSMSG_STAFF_HELPERS");
5599 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
| FLAGS_SERVICE
| FLAGS_BOT
);
5603 static CHANSERV_FUNC(cmd_staff
)
5605 reply("CSMSG_NETWORK_STAFF");
5606 cmd_ircops(CSFUNC_ARGS
);
5607 cmd_helpers(CSFUNC_ARGS
);
5611 static CHANSERV_FUNC(cmd_peek
)
5613 struct modeNode
*mn
;
5614 char modes
[MODELEN
];
5616 struct helpfile_table table
;
5618 irc_make_chanmode(channel
, modes
);
5620 reply("CSMSG_PEEK_INFO", channel
->name
);
5621 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5623 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5624 reply("CSMSG_PEEK_MODES", modes
);
5625 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5629 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5630 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5631 for(n
= 0; n
< channel
->members
.used
; n
++)
5633 mn
= channel
->members
.list
[n
];
5634 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5636 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5637 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5642 reply("CSMSG_PEEK_OPS");
5643 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5646 reply("CSMSG_PEEK_NO_OPS");
5647 reply("CSMSG_PEEK_END");
5651 static MODCMD_FUNC(cmd_wipeinfo
)
5653 struct handle_info
*victim
;
5654 struct userData
*ud
, *actor
, *real_actor
;
5655 unsigned int override
= 0;
5658 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5659 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5660 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5662 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5664 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5667 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5669 reply("MSG_USER_OUTRANKED", victim
->handle
);
5672 if((ud
!= real_actor
) && (!real_actor
|| (ud
->access
>= real_actor
->access
)))
5673 override
= CMD_LOG_OVERRIDE
;
5677 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5678 return 1 | override
;
5682 resync_channel(struct chanNode
*channel
)
5684 struct mod_chanmode
*changes
;
5685 struct chanData
*cData
= channel
->channel_info
;
5686 unsigned int ii
, used
;
5688 /* 6 = worst case -ovh+ovh on everyone */
5689 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5690 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5692 struct modeNode
*mn
= channel
->members
.list
[ii
];
5693 struct userData
*uData
;
5695 if(IsService(mn
->user
))
5699 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5701 /* If the channel is in no-mode mode, de-mode EVERYONE */
5702 if(cData
->chOpts
[chAutomode
] == 'n')
5706 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5707 changes
->args
[used
++].u
.member
= mn
;
5710 else /* Give various userlevels their modes.. */
5712 /* If the user has autoop/autovoice disabled then ignore them */
5713 if(uData
&& !IsUserAutoOp(uData
))
5715 if(uData
&& uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
5717 if(!(mn
->modes
& MODE_VOICE
))
5719 changes
->args
[used
].mode
= MODE_VOICE
;
5720 changes
->args
[used
++].u
.member
= mn
;
5723 else if(uData
&& uData
->access
>= UL_OP
)
5725 if(!(mn
->modes
& MODE_CHANOP
))
5727 changes
->args
[used
].mode
= MODE_CHANOP
;
5728 changes
->args
[used
++].u
.member
= mn
;
5731 else if(uData
&& uData
->access
>= UL_HALFOP
)
5733 if(mn
->modes
& MODE_CHANOP
)
5735 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5736 changes
->args
[used
++].u
.member
= mn
;
5738 if(!(mn
->modes
& MODE_HALFOP
))
5740 changes
->args
[used
].mode
= MODE_HALFOP
;
5741 changes
->args
[used
++].u
.member
= mn
;
5744 else if(uData
&& uData
->access
>= UL_PEON
)
5746 if(mn
->modes
& MODE_CHANOP
)
5748 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5749 changes
->args
[used
++].u
.member
= mn
;
5751 if(mn
->modes
& MODE_HALFOP
)
5753 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5754 changes
->args
[used
++].u
.member
= mn
;
5756 /* Don't voice peons if were in mode m */
5757 if( cData
->chOpts
[chAutomode
] == 'm')
5759 if(mn
->modes
& MODE_VOICE
)
5761 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5762 changes
->args
[used
++].u
.member
= mn
;
5765 /* otherwise, make user they do have voice */
5766 else if(!(mn
->modes
& MODE_VOICE
))
5768 changes
->args
[used
].mode
= MODE_VOICE
;
5769 changes
->args
[used
++].u
.member
= mn
;
5772 else /* They arnt on the userlist.. */
5774 /* If we voice everyone, but they dont.. */
5775 if(cData
->chOpts
[chAutomode
] == 'v')
5777 /* Remove anything except v */
5778 if(mn
->modes
& ~MODE_VOICE
)
5780 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5781 changes
->args
[used
++].u
.member
= mn
;
5784 if(!(mn
->modes
& MODE_VOICE
))
5786 changes
->args
[used
].mode
= MODE_VOICE
;
5787 changes
->args
[used
++].u
.member
= mn
;
5790 /* If we hop everyone, but they dont.. */
5791 else if(cData
->chOpts
[chAutomode
] == 'h')
5793 /* Remove anything except h */
5794 if(mn
->modes
& ~MODE_HALFOP
)
5796 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5797 changes
->args
[used
++].u
.member
= mn
;
5800 if(!(mn
->modes
& MODE_HALFOP
))
5802 changes
->args
[used
].mode
= MODE_HALFOP
;
5803 changes
->args
[used
++].u
.member
= mn
;
5806 /* If we op everyone, but they dont.. */
5807 else if(cData
->chOpts
[chAutomode
] == 'o')
5809 /* Remove anything except h */
5810 if(mn
->modes
& ~MODE_CHANOP
)
5812 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5813 changes
->args
[used
++].u
.member
= mn
;
5816 if(!(mn
->modes
& MODE_CHANOP
))
5818 changes
->args
[used
].mode
= MODE_CHANOP
;
5819 changes
->args
[used
++].u
.member
= mn
;
5822 /* they have no excuse for having modes, de-everything them */
5827 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5828 changes
->args
[used
++].u
.member
= mn
;
5834 changes
->argc
= used
;
5835 mod_chanmode_announce(chanserv
, channel
, changes
);
5836 mod_chanmode_free(changes
);
5839 static CHANSERV_FUNC(cmd_resync
)
5841 resync_channel(channel
);
5842 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5846 static CHANSERV_FUNC(cmd_seen
)
5848 struct userData
*uData
;
5849 struct handle_info
*handle
;
5850 char seen
[INTERVALLEN
];
5854 if(!irccasecmp(argv
[1], chanserv
->nick
))
5856 reply("CSMSG_IS_CHANSERV");
5860 if(!(handle
= get_handle_info(argv
[1])))
5862 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5866 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5868 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5873 reply("CSMSG_USER_PRESENT", handle
->handle
);
5874 else if(uData
->seen
)
5875 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5877 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5879 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5880 reply("CSMSG_USER_VACATION", handle
->handle
);
5885 static MODCMD_FUNC(cmd_names
)
5887 struct userNode
*targ
;
5888 struct userData
*targData
;
5889 unsigned int ii
, pos
;
5892 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5894 targ
= channel
->members
.list
[ii
]->user
;
5895 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5898 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5901 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5905 if(IsUserSuspended(targData
))
5907 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5910 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5911 reply("CSMSG_END_NAMES", channel
->name
);
5916 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5918 switch(ntype
->visible_type
)
5920 case NOTE_VIS_ALL
: return 1;
5921 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5922 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5927 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5929 struct userData
*uData
;
5931 switch(ntype
->set_access_type
)
5933 case NOTE_SET_CHANNEL_ACCESS
:
5934 if(!user
->handle_info
)
5936 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5938 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5939 case NOTE_SET_CHANNEL_SETTER
:
5940 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5941 case NOTE_SET_PRIVILEGED
: default:
5942 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5946 static CHANSERV_FUNC(cmd_note
)
5948 struct chanData
*cData
;
5950 struct note_type
*ntype
;
5952 cData
= channel
->channel_info
;
5955 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5959 /* If no arguments, show all visible notes for the channel. */
5965 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5967 note
= iter_data(it
);
5968 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5971 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5972 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5975 reply("CSMSG_NOTELIST_END", channel
->name
);
5977 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5979 /* If one argument, show the named note. */
5982 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5983 && note_type_visible_to_user(cData
, note
->type
, user
))
5985 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5987 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5988 && note_type_visible_to_user(NULL
, ntype
, user
))
5990 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5995 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5999 /* Assume they're trying to set a note. */
6003 ntype
= dict_find(note_types
, argv
[1], NULL
);
6006 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
6009 else if(note_type_settable_by_user(channel
, ntype
, user
))
6011 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
6012 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
6013 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
6014 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
6015 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
6017 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
6019 /* The note is viewable to staff only, so return 0
6020 to keep the invocation from getting logged (or
6021 regular users can see it in !events). */
6027 reply("CSMSG_NO_ACCESS");
6034 static CHANSERV_FUNC(cmd_delnote
)
6039 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
6040 || !note_type_settable_by_user(channel
, note
->type
, user
))
6042 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
6045 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
6046 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
6050 static CHANSERV_FUNC(cmd_last
)
6056 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
6058 if(numoflines
< 1 || numoflines
> 200)
6060 reply("CSMSG_LAST_INVALID");
6063 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
6067 static CHANSERV_FUNC(cmd_events
)
6069 struct logSearch discrim
;
6070 struct logReport report
;
6071 unsigned int matches
, limit
;
6073 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
6074 if(limit
< 1 || limit
> 200)
6077 memset(&discrim
, 0, sizeof(discrim
));
6078 discrim
.masks
.bot
= chanserv
;
6079 discrim
.masks
.channel_name
= channel
->name
;
6081 discrim
.masks
.command
= argv
[2];
6082 discrim
.limit
= limit
;
6083 discrim
.max_time
= INT_MAX
;
6084 discrim
.severities
= 1 << LOG_COMMAND
;
6085 report
.reporter
= chanserv
;
6087 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
6088 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6090 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
6092 reply("MSG_MATCH_COUNT", matches
);
6094 reply("MSG_NO_MATCHES");
6098 static CHANSERV_FUNC(cmd_say
)
6104 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6105 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
6107 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6109 struct handle_info
*hi
;
6110 struct userNode
*authed
;
6113 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6115 if (!(hi
= get_handle_info(argv
[1] + 1)))
6117 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6121 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6122 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "%s", msg
);
6124 else if(GetUserH(argv
[1]))
6127 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6128 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
6132 reply("MSG_NOT_TARGET_NAME");
6138 static CHANSERV_FUNC(cmd_emote
)
6144 /* CTCP is so annoying. */
6145 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6146 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6148 else if(*argv
[1] == '*' && argv
[1][1] != '\0')
6150 struct handle_info
*hi
;
6151 struct userNode
*authed
;
6154 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6156 if (!(hi
= get_handle_info(argv
[1] + 1)))
6158 reply("MSG_HANDLE_UNKNOWN", argv
[1] + 1);
6162 for (authed
= hi
->users
; authed
; authed
= authed
->next_authed
)
6163 send_target_message(5, authed
->nick
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6165 else if(GetUserH(argv
[1]))
6167 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
6168 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
6172 reply("MSG_NOT_TARGET_NAME");
6178 struct channelList
*
6179 chanserv_support_channels(void)
6181 return &chanserv_conf
.support_channels
;
6184 static CHANSERV_FUNC(cmd_expire
)
6186 int channel_count
= registered_channels
;
6187 expire_channels(NULL
);
6188 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
6193 chanserv_expire_suspension(void *data
)
6195 struct suspended
*suspended
= data
;
6196 struct chanNode
*channel
;
6199 /* Update the channel registration data structure. */
6200 if(!suspended
->expires
|| (now
< suspended
->expires
))
6201 suspended
->revoked
= now
;
6202 channel
= suspended
->cData
->channel
;
6203 suspended
->cData
->channel
= channel
;
6204 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
6206 /* If appropriate, re-join ChanServ to the channel. */
6207 if(!IsOffChannel(suspended
->cData
))
6209 spamserv_cs_suspend(channel
, 0, 0, NULL
);
6210 ss_cs_join_channel(channel
, 1);
6213 /* Mark everyone currently in the channel as present. */
6214 for(ii
= 0; ii
< channel
->members
.used
; ++ii
)
6216 struct userData
*uData
= GetChannelAccess(suspended
->cData
, channel
->members
.list
[ii
]->user
->handle_info
);
6225 static CHANSERV_FUNC(cmd_csuspend
)
6227 struct suspended
*suspended
;
6228 char reason
[MAXLEN
];
6229 time_t expiry
, duration
;
6230 struct userData
*uData
;
6234 if(IsProtected(channel
->channel_info
))
6236 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
6240 if(argv
[1][0] == '!')
6242 else if(IsSuspended(channel
->channel_info
))
6244 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
6245 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
6249 if(!strcmp(argv
[1], "0"))
6251 else if((duration
= ParseInterval(argv
[1])))
6252 expiry
= now
+ duration
;
6255 reply("MSG_INVALID_DURATION", argv
[1]);
6259 unsplit_string(argv
+ 2, argc
- 2, reason
);
6261 suspended
= calloc(1, sizeof(*suspended
));
6262 suspended
->revoked
= 0;
6263 suspended
->issued
= now
;
6264 suspended
->suspender
= strdup(user
->handle_info
->handle
);
6265 suspended
->expires
= expiry
;
6266 suspended
->reason
= strdup(reason
);
6267 suspended
->cData
= channel
->channel_info
;
6268 suspended
->previous
= suspended
->cData
->suspended
;
6269 suspended
->cData
->suspended
= suspended
;
6271 if(suspended
->expires
)
6272 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6274 if(IsSuspended(channel
->channel_info
))
6276 suspended
->previous
->revoked
= now
;
6277 if(suspended
->previous
->expires
)
6278 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
6280 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
6281 channel
->name
, suspended
->suspender
);
6285 /* Mark all users in channel as absent. */
6286 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
6295 /* Mark the channel as suspended, then part. */
6296 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
6297 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
6298 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
6299 reply("CSMSG_SUSPENDED", channel
->name
);
6300 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
6301 channel
->name
, suspended
->suspender
);
6306 static CHANSERV_FUNC(cmd_cunsuspend
)
6308 struct suspended
*suspended
;
6310 if(!IsSuspended(channel
->channel_info
))
6312 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
6316 suspended
= channel
->channel_info
->suspended
;
6318 /* Expire the suspension and join ChanServ to the channel. */
6319 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
6320 chanserv_expire_suspension(suspended
);
6321 reply("CSMSG_UNSUSPENDED", channel
->name
);
6322 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
6323 channel
->name
, user
->handle_info
->handle
);
6327 typedef struct chanservSearch
6335 unsigned long flags
;
6339 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
6342 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
6347 search
= malloc(sizeof(struct chanservSearch
));
6348 memset(search
, 0, sizeof(*search
));
6351 for(i
= 0; i
< argc
; i
++)
6353 /* Assume all criteria require arguments. */
6356 reply("MSG_MISSING_PARAMS", argv
[i
]);
6360 if(!irccasecmp(argv
[i
], "name"))
6361 search
->name
= argv
[++i
];
6362 else if(!irccasecmp(argv
[i
], "registrar"))
6363 search
->registrar
= argv
[++i
];
6364 else if(!irccasecmp(argv
[i
], "unvisited"))
6365 search
->unvisited
= ParseInterval(argv
[++i
]);
6366 else if(!irccasecmp(argv
[i
], "registered"))
6367 search
->registered
= ParseInterval(argv
[++i
]);
6368 else if(!irccasecmp(argv
[i
], "flags"))
6371 if(!irccasecmp(argv
[i
], "nodelete"))
6372 search
->flags
|= CHANNEL_NODELETE
;
6373 else if(!irccasecmp(argv
[i
], "suspended"))
6374 search
->flags
|= CHANNEL_SUSPENDED
;
6375 else if(!irccasecmp(argv
[i
], "unreviewed"))
6376 search
->flags
|= CHANNEL_UNREVIEWED
;
6379 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
6383 else if(!irccasecmp(argv
[i
], "limit"))
6384 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
6387 reply("MSG_INVALID_CRITERIA", argv
[i
]);
6392 if(search
->name
&& !strcmp(search
->name
, "*"))
6394 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
6395 search
->registrar
= 0;
6404 chanserv_channel_match(struct chanData
*channel
, search_t search
)
6406 const char *name
= channel
->channel
->name
;
6407 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
6408 (search
->registrar
&& !channel
->registrar
) ||
6409 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
6410 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
6411 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
6412 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
6419 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
6421 struct chanData
*channel
;
6422 unsigned int matches
= 0;
6424 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
6426 if(!chanserv_channel_match(channel
, search
))
6436 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
6441 search_print(struct chanData
*channel
, void *data
)
6443 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
6446 static CHANSERV_FUNC(cmd_search
)
6449 unsigned int matches
;
6450 channel_search_func action
;
6454 if(!irccasecmp(argv
[1], "count"))
6455 action
= search_count
;
6456 else if(!irccasecmp(argv
[1], "print"))
6457 action
= search_print
;
6460 reply("CSMSG_ACTION_INVALID", argv
[1]);
6464 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
6468 if(action
== search_count
)
6469 search
->limit
= INT_MAX
;
6471 if(action
== search_print
)
6473 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6474 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6478 matches
= chanserv_channel_search(search
, action
, user
);
6481 reply("MSG_MATCH_COUNT", matches
);
6483 reply("MSG_NO_MATCHES");
6489 static CHANSERV_FUNC(cmd_unvisited
)
6491 struct chanData
*cData
;
6492 time_t interval
= chanserv_conf
.channel_expire_delay
;
6493 char buffer
[INTERVALLEN
];
6494 unsigned int limit
= 25, matches
= 0;
6498 interval
= ParseInterval(argv
[1]);
6500 limit
= atoi(argv
[2]);
6503 intervalString(buffer
, interval
, user
->handle_info
);
6504 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
6506 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
6508 if((now
- cData
->visited
) < interval
)
6511 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
6512 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
6519 static MODCMD_FUNC(chan_opt_unreviewed
)
6521 struct chanData
*cData
= channel
->channel_info
;
6522 int value
= (cData
->flags
& CHANNEL_UNREVIEWED
) ? 1 : 0;
6528 /* The two directions can have different ACLs. */
6529 if(enabled_string(argv
[1]))
6531 else if(disabled_string(argv
[1]))
6535 reply("MSG_INVALID_BINARY", argv
[1]);
6539 if (new_value
!= value
)
6541 struct svccmd
*subcmd
;
6542 char subcmd_name
[32];
6544 snprintf(subcmd_name
, sizeof(subcmd_name
), "%s %s", argv
[0], (new_value
? "on" : "off"));
6545 subcmd
= dict_find(cmd
->parent
->commands
, subcmd_name
, NULL
);
6548 reply("MSG_COMMAND_DISABLED", subcmd_name
);
6551 else if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
6555 cData
->flags
|= CHANNEL_UNREVIEWED
;
6558 free(cData
->registrar
);
6559 cData
->registrar
= strdup(user
->handle_info
->handle
);
6560 cData
->flags
&= ~CHANNEL_UNREVIEWED
;
6567 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_ON"));
6569 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_OFF"));
6573 static MODCMD_FUNC(chan_opt_defaulttopic
)
6579 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6581 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6585 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6587 free(channel
->channel_info
->topic
);
6588 if(topic
[0] == '*' && topic
[1] == 0)
6590 topic
= channel
->channel_info
->topic
= NULL
;
6594 topic
= channel
->channel_info
->topic
= strdup(topic
);
6595 if(channel
->channel_info
->topic_mask
6596 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6597 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6599 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6602 if(channel
->channel_info
->topic
)
6603 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6605 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6609 static MODCMD_FUNC(chan_opt_topicmask
)
6613 struct chanData
*cData
= channel
->channel_info
;
6616 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6618 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6622 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6624 if(cData
->topic_mask
)
6625 free(cData
->topic_mask
);
6626 if(mask
[0] == '*' && mask
[1] == 0)
6628 cData
->topic_mask
= 0;
6632 cData
->topic_mask
= strdup(mask
);
6634 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6635 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6636 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6640 if(channel
->channel_info
->topic_mask
)
6641 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6643 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6647 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6651 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6655 if(greeting
[0] == '*' && greeting
[1] == 0)
6659 unsigned int length
= strlen(greeting
);
6660 if(length
> chanserv_conf
.greeting_length
)
6662 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6665 *data
= strdup(greeting
);
6674 reply(name
, user_find_message(user
, "MSG_NONE"));
6678 static MODCMD_FUNC(chan_opt_greeting
)
6680 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6683 static MODCMD_FUNC(chan_opt_usergreeting
)
6685 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6688 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6690 unsigned int charmax
;
6694 charmax
= atoi(argv
[1]);
6695 if ((charmax
> 0) && (charmax
<= chanserv_conf
.max_userinfo_length
))
6696 channel
->channel_info
->maxsetinfo
= charmax
;
6699 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6703 static MODCMD_FUNC(chan_opt_modes
)
6705 struct mod_chanmode
*new_modes
;
6706 char modes
[MODELEN
];
6710 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6711 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6715 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6717 reply("CSMSG_NO_ACCESS");
6720 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6722 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6724 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6726 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6729 else if(new_modes
->argc
> 1)
6731 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6732 mod_chanmode_free(new_modes
);
6737 channel
->channel_info
->modes
= *new_modes
;
6738 modcmd_chanmode_announce(new_modes
);
6739 mod_chanmode_free(new_modes
);
6743 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6745 reply("CSMSG_SET_MODES", modes
);
6747 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6751 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6753 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6755 struct chanData
*cData
= channel
->channel_info
;
6760 /* Set flag according to value. */
6761 if(enabled_string(argv
[1]))
6763 cData
->flags
|= mask
;
6766 else if(disabled_string(argv
[1]))
6768 cData
->flags
&= ~mask
;
6773 reply("MSG_INVALID_BINARY", argv
[1]);
6779 /* Find current option value. */
6780 value
= (cData
->flags
& mask
) ? 1 : 0;
6784 reply(name
, user_find_message(user
, "MSG_ON"));
6786 reply(name
, user_find_message(user
, "MSG_OFF"));
6790 static MODCMD_FUNC(chan_opt_nodelete
)
6792 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6794 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6798 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6801 static MODCMD_FUNC(chan_opt_dynlimit
)
6803 struct mod_chanmode change
;
6807 if (disabled_string(argv
[1]))
6809 mod_chanmode_init(&change
);
6810 change
.modes_clear
|= MODE_LIMIT
;
6811 mod_chanmode_announce(chanserv
, channel
, &change
);
6815 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6818 static MODCMD_FUNC(chan_opt_offchannel
)
6820 struct chanData
*cData
= channel
->channel_info
;
6825 /* Set flag according to value. */
6826 if(enabled_string(argv
[1]))
6828 if(!IsOffChannel(cData
))
6829 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6830 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6833 else if(disabled_string(argv
[1]))
6835 if(IsOffChannel(cData
))
6837 struct mod_chanmode change
;
6838 mod_chanmode_init(&change
);
6840 change
.args
[0].mode
= MODE_CHANOP
;
6841 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6842 mod_chanmode_announce(chanserv
, channel
, &change
);
6844 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6849 reply("MSG_INVALID_BINARY", argv
[1]);
6855 /* Find current option value. */
6856 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6860 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6862 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6866 static MODCMD_FUNC(chan_opt_defaults
)
6868 struct userData
*uData
;
6869 struct chanData
*cData
;
6870 const char *confirm
;
6871 enum levelOption lvlOpt
;
6872 enum charOption chOpt
;
6874 cData
= channel
->channel_info
;
6875 uData
= GetChannelUser(cData
, user
->handle_info
);
6876 if(!uData
|| (uData
->access
< UL_OWNER
))
6878 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6881 confirm
= make_confirmation_string(uData
);
6882 if((argc
< 2) || strcmp(argv
[1], confirm
))
6884 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6887 cData
->flags
= (CHANNEL_DEFAULT_FLAGS
& ~CHANNEL_PRESERVED_FLAGS
)
6888 | (cData
->flags
& CHANNEL_PRESERVED_FLAGS
);
6889 cData
->modes
= chanserv_conf
.default_modes
;
6890 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6891 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6892 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6893 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6894 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6899 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6901 struct chanData
*cData
= channel
->channel_info
;
6902 struct userData
*uData
;
6903 unsigned short value
;
6907 if(!check_user_level(channel
, user
, option
, 1, 1))
6909 reply("CSMSG_CANNOT_SET");
6912 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6913 if(!value
&& strcmp(argv
[1], "0"))
6915 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6918 uData
= GetChannelUser(cData
, user
->handle_info
);
6919 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6921 reply("CSMSG_BAD_SETLEVEL");
6927 /* This test only applies to owners, since non-owners
6928 * trying to set an option to above their level get caught
6929 * by the CSMSG_BAD_SETLEVEL test above.
6931 if(value
> uData
->access
)
6933 reply("CSMSG_BAD_SETTERS");
6940 cData
->lvlOpts
[option
] = value
;
6942 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6946 static MODCMD_FUNC(chan_opt_enfops
)
6948 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6951 static MODCMD_FUNC(chan_opt_enfhalfops
)
6953 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6955 static MODCMD_FUNC(chan_opt_enfmodes
)
6957 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6960 static MODCMD_FUNC(chan_opt_enftopic
)
6962 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6965 static MODCMD_FUNC(chan_opt_pubcmd
)
6967 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6970 static MODCMD_FUNC(chan_opt_setters
)
6972 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6975 static MODCMD_FUNC(chan_opt_userinfo
)
6977 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6980 static MODCMD_FUNC(chan_opt_topicsnarf
)
6982 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6985 static MODCMD_FUNC(chan_opt_inviteme
)
6987 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6990 /* TODO: Make look like this when no args are
6992 * -X3- -------------------------------
6993 * -X3- BanTimeout: Bans are removed:
6994 * -X3- ----- * indicates current -----
6995 * -X3- 0: [*] Never.
6996 * -X3- 1: [ ] After 10 minutes.
6997 * -X3- 2: [ ] After 2 hours.
6998 * -X3- 3: [ ] After 4 hours.
6999 * -X3- 4: [ ] After 24 hours.
7000 * -X3- 5: [ ] After one week.
7001 * -X3- ------------- End -------------
7004 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
7006 struct chanData
*cData
= channel
->channel_info
;
7007 int count
= charOptions
[option
].count
, idx
;
7011 idx
= atoi(argv
[1]);
7013 if(!isdigit(argv
[1][0]) || (idx
< 0) || (idx
>= count
))
7015 reply("CSMSG_INVALID_NUMERIC", idx
);
7016 /* Show possible values. */
7017 for(idx
= 0; idx
< count
; idx
++)
7018 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
7022 cData
->chOpts
[option
] = charOptions
[option
].values
[idx
].value
;
7026 /* Find current option value. */
7029 (idx
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[idx
].value
);
7033 /* Somehow, the option value is corrupt; reset it to the default. */
7034 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
7039 reply(charOptions
[option
].format_name
, idx
, user_find_message(user
, charOptions
[option
].values
[idx
].format_name
));
7043 static MODCMD_FUNC(chan_opt_automode
)
7045 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
7048 static MODCMD_FUNC(chan_opt_protect
)
7050 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
7053 static MODCMD_FUNC(chan_opt_toys
)
7055 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
7058 static MODCMD_FUNC(chan_opt_ctcpreaction
)
7060 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
7063 static MODCMD_FUNC(chan_opt_bantimeout
)
7065 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
7068 static MODCMD_FUNC(chan_opt_topicrefresh
)
7070 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
7073 static MODCMD_FUNC(chan_opt_resync
)
7075 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
7078 static struct svccmd_list set_shows_list
;
7081 handle_svccmd_unbind(struct svccmd
*target
, UNUSED_ARG(void *extra
)) {
7083 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
7084 if(target
== set_shows_list
.list
[ii
])
7085 set_shows_list
.used
= 0;
7088 static CHANSERV_FUNC(cmd_set
)
7090 struct svccmd
*subcmd
;
7094 /* Check if we need to (re-)initialize set_shows_list. */
7095 if(!set_shows_list
.used
)
7097 if(!set_shows_list
.size
)
7099 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
7100 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
7102 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
7104 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
7105 sprintf(buf
, "%s %s", argv
[0], name
);
7106 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7109 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
7112 svccmd_list_append(&set_shows_list
, subcmd
);
7118 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
7119 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
7121 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
7123 subcmd
= set_shows_list
.list
[ii
];
7124 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
7126 reply("CSMSG_CHANNEL_OPTIONS_END");
7130 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7131 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7134 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7137 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
7139 reply("CSMSG_NO_ACCESS");
7145 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7149 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
7151 struct userData
*uData
;
7153 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7156 reply("CSMSG_NOT_USER", channel
->name
);
7162 /* Just show current option value. */
7164 else if(enabled_string(argv
[1]))
7166 uData
->flags
|= mask
;
7168 else if(disabled_string(argv
[1]))
7170 uData
->flags
&= ~mask
;
7174 reply("MSG_INVALID_BINARY", argv
[1]);
7178 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
7182 static MODCMD_FUNC(user_opt_autoop
)
7184 struct userData
*uData
;
7186 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7189 reply("CSMSG_NOT_USER", channel
->name
);
7192 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
7193 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
7195 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
7198 static MODCMD_FUNC(user_opt_autoinvite
)
7200 if((argc
> 1) && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
7202 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
7204 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
7207 static MODCMD_FUNC(user_opt_autojoin
)
7209 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
7212 static MODCMD_FUNC(user_opt_info
)
7214 struct userData
*uData
;
7217 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7221 /* If they got past the command restrictions (which require access)
7222 * but fail this test, we have some fool with security override on.
7224 reply("CSMSG_NOT_USER", channel
->name
);
7231 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
7232 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
7234 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
7237 bp
= strcspn(infoline
, "\001");
7240 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
7245 if(infoline
[0] == '*' && infoline
[1] == 0)
7248 uData
->info
= strdup(infoline
);
7251 reply("CSMSG_USET_INFO", uData
->info
);
7253 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
7257 struct svccmd_list uset_shows_list
;
7259 static CHANSERV_FUNC(cmd_uset
)
7261 struct svccmd
*subcmd
;
7265 /* Check if we need to (re-)initialize uset_shows_list. */
7266 if(!uset_shows_list
.used
)
7270 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7273 if(!uset_shows_list
.size
)
7275 uset_shows_list
.size
= ArrayLength(options
);
7276 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
7278 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
7280 const char *name
= options
[ii
];
7281 sprintf(buf
, "%s %s", argv
[0], name
);
7282 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7285 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
7288 svccmd_list_append(&uset_shows_list
, subcmd
);
7294 /* Do this so options are presented in a consistent order. */
7295 reply("CSMSG_USER_OPTIONS");
7296 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
7297 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
7301 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7302 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7305 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7309 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7312 static CHANSERV_FUNC(cmd_giveownership
)
7314 struct handle_info
*new_owner_hi
;
7315 struct userData
*new_owner
;
7316 struct userData
*curr_user
;
7317 struct userData
*invoker
;
7318 struct chanData
*cData
= channel
->channel_info
;
7319 struct do_not_register
*dnr
;
7320 struct giveownership
*giveownership
;
7321 const char *confirm
;
7322 unsigned int force
, override
;
7323 unsigned short co_access
, new_owner_old_access
;
7324 char transfer_reason
[MAXLEN
];
7327 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
7328 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
7330 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
7331 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
7332 && (uData
->access
> 500)
7333 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
7334 || uData
->access
< 500));
7337 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
7339 struct userData
*owner
= NULL
;
7340 for(curr_user
= channel
->channel_info
->users
;
7342 curr_user
= curr_user
->next
)
7344 if(curr_user
->access
!= UL_OWNER
)
7348 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
7355 else if(!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
7357 char delay
[INTERVALLEN
];
7358 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
7359 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
7363 reply("CSMSG_NO_OWNER", channel
->name
);
7366 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
7368 if(new_owner_hi
== user
->handle_info
)
7370 reply("CSMSG_NO_TRANSFER_SELF");
7373 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
7378 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_OWNER
- 1, 0, NULL
, 0);
7382 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
7386 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
7388 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
7391 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
7392 if(!IsHelping(user
))
7393 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
7395 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
7399 invoker
= GetChannelUser(cData
, user
->handle_info
);
7400 if(invoker
->access
<= UL_OWNER
)
7402 confirm
= make_confirmation_string(curr_user
);
7403 if((argc
< 3) || strcmp(argv
[2], confirm
))
7405 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi
->handle
, confirm
);
7410 new_owner_old_access
= new_owner
->access
;
7411 if(new_owner
->access
>= UL_COOWNER
)
7412 co_access
= new_owner
->access
;
7414 co_access
= UL_COOWNER
;
7415 new_owner
->access
= UL_OWNER
;
7417 curr_user
->access
= co_access
;
7418 cData
->ownerTransfer
= now
;
7420 giveownership
= calloc(1, sizeof(*giveownership
));
7421 giveownership
->issued
= now
;
7422 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
7423 giveownership
->target
= strdup(new_owner_hi
->handle
);
7424 giveownership
->target_access
= new_owner_old_access
;
7427 if(argc
> (2 + force
))
7429 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
7430 giveownership
->reason
= strdup(transfer_reason
);
7432 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
7435 giveownership
->previous
= channel
->channel_info
->giveownership
;
7436 channel
->channel_info
->giveownership
= giveownership
;
7438 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
7439 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
7440 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
7445 chanserv_expire_user_suspension(void *data
)
7447 struct userData
*target
= data
;
7449 target
->expires
= 0;
7450 target
->flags
&= ~USER_SUSPENDED
;
7453 static CHANSERV_FUNC(cmd_suspend
)
7455 struct handle_info
*hi
;
7456 struct userData
*actor
, *real_actor
, *target
;
7457 unsigned int override
= 0;
7461 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7462 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7463 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7464 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7466 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7469 if(target
->access
>= actor
->access
)
7471 reply("MSG_USER_OUTRANKED", hi
->handle
);
7474 if(target
->flags
& USER_SUSPENDED
)
7476 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
7481 target
->present
= 0;
7484 if(!strcmp(argv
[2], "0"))
7488 unsigned int duration
;
7489 if(!(duration
= ParseInterval(argv
[2])))
7491 reply("MSG_INVALID_DURATION", argv
[2]);
7494 expiry
= now
+ duration
;
7497 target
->expires
= expiry
;
7500 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
7502 if(!real_actor
|| target
->access
>= real_actor
->access
)
7503 override
= CMD_LOG_OVERRIDE
;
7504 target
->flags
|= USER_SUSPENDED
;
7505 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
7506 return 1 | override
;
7509 static CHANSERV_FUNC(cmd_unsuspend
)
7511 struct handle_info
*hi
;
7512 struct userData
*actor
= NULL
, *real_actor
= NULL
, *target
;
7513 unsigned int override
= 0;
7516 if(!(hi
= modcmd_get_handle_info(user
, argv
[1])))
7518 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7519 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7520 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7522 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7525 if(target
->access
>= actor
->access
)
7527 reply("MSG_USER_OUTRANKED", hi
->handle
);
7530 if(!(target
->flags
& USER_SUSPENDED
))
7532 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
7535 if(!real_actor
|| target
->access
>= real_actor
->access
)
7536 override
= CMD_LOG_OVERRIDE
;
7537 target
->flags
&= ~USER_SUSPENDED
;
7538 scan_user_presence(target
, NULL
);
7539 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
7540 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
7541 return 1 | override
;
7544 static MODCMD_FUNC(cmd_deleteme
)
7546 struct handle_info
*hi
;
7547 struct userData
*target
;
7548 const char *confirm_string
;
7549 unsigned short access
;
7552 hi
= user
->handle_info
;
7553 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7555 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7558 if(target
->access
== UL_OWNER
)
7560 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
7563 confirm_string
= make_confirmation_string(target
);
7564 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
7566 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
7569 access
= target
->access
;
7570 channel_name
= strdup(channel
->name
);
7571 del_channel_user(target
, 1);
7572 reply("CSMSG_DELETED_YOU", access
, channel_name
);
7578 chanserv_refresh_topics(UNUSED_ARG(void *data
))
7580 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7581 struct chanData
*cData
;
7584 for(cData
= channelList
; cData
; cData
= cData
->next
)
7586 if(IsSuspended(cData
))
7588 opt
= cData
->chOpts
[chTopicRefresh
];
7591 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
7594 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
7595 cData
->last_refresh
= refresh_num
;
7597 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
7601 chanserv_auto_resync(UNUSED_ARG(void *data
))
7603 unsigned int refresh_num
= (now
- self
->link_time
) / chanserv_conf
.refresh_period
;
7604 struct chanData
*cData
;
7607 for(cData
= channelList
; cData
; cData
= cData
->next
)
7609 if(IsSuspended(cData
))
7611 opt
= cData
->chOpts
[chResync
];
7614 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1')))
7616 resync_channel(cData
->channel
);
7617 cData
->last_resync
= refresh_num
;
7619 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7622 static CHANSERV_FUNC(cmd_unf
)
7626 char response
[MAXLEN
];
7627 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7628 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7629 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7632 reply("CSMSG_UNF_RESPONSE");
7636 static CHANSERV_FUNC(cmd_ping
)
7640 char response
[MAXLEN
];
7641 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7642 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7643 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7646 reply("CSMSG_PING_RESPONSE");
7650 static CHANSERV_FUNC(cmd_wut
)
7654 char response
[MAXLEN
];
7655 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7656 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7657 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7660 reply("CSMSG_WUT_RESPONSE");
7664 static CHANSERV_FUNC(cmd_roulette
)
7668 struct chanData
*cData
= channel
->channel_info
;
7672 if (cData
->roulette_chamber
)
7674 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7678 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7679 cData
->roulette_chamber
= 1 + rand() % 6;
7685 static CHANSERV_FUNC(cmd_shoot
)
7689 struct chanData
*cData
= channel
->channel_info
;
7691 if (cData
->roulette_chamber
<= 0)
7693 struct service
*service
;
7694 if ((service
= service_find(chanserv
->nick
)))
7696 reply("CSMSG_ROULETTE_NEW", service
->trigger
);
7701 cData
->roulette_chamber
--;
7703 if (cData
->roulette_chamber
== 0)
7705 reply("CSMSG_ROULETTE_BANG");
7706 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7707 DelUser(user
, chanserv
, 1, "BANG!!!!");
7710 reply("CSMSG_ROULETTE_CLICK");
7717 chanserv_remove_abuse(void *data
)
7719 char *remnick
= data
;
7720 struct userNode
*user
;
7721 /* sometimes the clone was killed and maybe even the user took their nick back
7722 * (ie, an oper) so dont kill them here after all unless they are local. */
7723 if( (user
= GetUserH(remnick
)) )
7725 DelUser(user
, NULL
, 1, "");
7728 int lamepart(struct userNode
*nick
) {
7729 struct modeNode
*mn
;
7730 unsigned int count
, n
;
7732 for (n
=count
=0; n
<nick
->channels
.used
; n
++)
7734 mn
= nick
->channels
.list
[n
];
7735 irc_svspart(chanserv
, nick
, mn
->channel
);
7741 static CHANSERV_FUNC(cmd_spin
)
7746 int type
= 0, lamep
= 1;
7749 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7757 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7758 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7759 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7761 if(chanserv_conf
.wheel
->used
< 1)
7763 /* wheel actions not defined! eek */
7767 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7768 if(!wheel
&& *wheel
)
7771 /* connection reset by peer */
7772 if (!strcasecmp(wheel
, "peer"))
7774 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7776 irc_kill(chanserv
, user
, "Connection reset by peer");
7778 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7780 /* part all channels */
7781 else if (!strcasecmp(wheel
, "partall"))
7783 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7787 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7789 /* random time gline */
7790 else if (!strcasecmp(wheel
, "gline"))
7792 char target
[HOSTLEN
+ 3];
7793 int wtime
= 120 + rand() % 600;
7795 strcpy(target
, "*@");
7796 strcat(target
, user
->hostname
);
7797 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7799 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1, 0);
7802 else if (!strcasecmp(wheel
, "shun"))
7804 char target
[HOSTLEN
+ 3];
7805 int wtime
= 120 + rand() % 600;
7807 strcpy(target
, "*@");
7808 strcat(target
, user
->hostname
);
7809 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SHUN");
7811 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1);
7813 /* absolutely nothing */
7814 else if (!strcasecmp(wheel
, "nothing"))
7816 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7818 /* join random chans and part em several times */
7819 else if (!strcasecmp(wheel
, "randjoin"))
7826 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7827 while(complete
!= 1)
7831 chango
= 120 + rand() % 600;
7832 sputsock("%s SJ %s #%d "FMT_TIME_T
, self
->numeric
, user
->numeric
, chango
, now
);
7842 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7851 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7857 /* abuse line added to /whois */
7858 else if (!strcasecmp(wheel
, "abusewhois"))
7860 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7861 irc_swhois(chanserv
, user
, "is being defecated on by services");
7863 /* kick from each channel your in */
7864 else if (!strcasecmp(wheel
, "kickall"))
7866 unsigned int count
, n
;
7867 struct modeNode
*mn
;
7869 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7871 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7872 mn
= user
->channels
.list
[n
];
7873 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7876 /* random nick change */
7877 else if (!strcasecmp(wheel
, "nickchange"))
7879 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7881 char *oldnick
= NULL
;
7882 char *oldident
= NULL
;
7883 char abusednick
[NICKLEN
] = "";
7884 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7885 struct userNode
*clone
;
7887 oldnick
= strdup(user
->nick
);
7888 oldident
= strdup(user
->ident
);
7890 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7893 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7894 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7895 if(!GetUserH(abusednick
))
7899 SVSNickChange(user
, abusednick
);
7900 irc_svsnick(chanserv
, user
, abusednick
);
7901 clone
= AddLocalUser(oldnick
, oldident
, "abused.by.wheel.of.misfortune", "I got abused by the wheel of misfortune :D", "+i");
7902 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7905 else if (!strcasecmp(wheel
, "kill"))
7907 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7909 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7910 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7912 /* service ignore */
7913 else if (!strcasecmp(wheel
, "svsignore"))
7917 char target
[HOSTLEN
+ 13];
7920 /* we cant gag opers, so just verbally abuse them */
7921 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7924 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7926 strcpy(target
, "*!*@");
7927 strcat(target
, user
->hostname
);
7928 ignoretime
= now
+ (1 + rand() % 120);
7930 gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7932 /* kick and ban from each channel your in */
7933 else if (!strcasecmp(wheel
, "kickbanall"))
7935 unsigned int count
, n
;
7936 struct modeNode
*mn
;
7937 //char ban[HOSTLEN + 1];
7939 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7941 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7942 for (n
=count
=0; n
<user
->channels
.used
; n
++)
7944 struct mod_chanmode
*change
;
7945 /* struct banData *bData; */
7946 unsigned int exists
;
7947 /* int duration = 300; */
7950 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7952 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7953 mn
= user
->channels
.list
[n
];
7954 if(mn
->channel
->banlist
.used
>= MAXBANS
)
7956 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7961 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7963 change
= mod_chanmode_alloc(1);
7964 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7965 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7968 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7969 mod_chanmode_free(change
);
7971 exists
= ChannelBanExists(mn
->channel
, ban
);
7973 change
= mod_chanmode_alloc(1);
7974 change
->args
[0].mode
= MODE_BAN
;
7975 change
->args
[0].u
.hostmask
= ban
;
7977 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7978 mod_chanmode_free(change
);
7982 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7986 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7991 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7998 static CHANSERV_FUNC(cmd_8ball
)
8000 unsigned int i
, j
, accum
;
8005 for(i
=1; i
<argc
; i
++)
8006 for(j
=0; argv
[i
][j
]; j
++)
8007 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
8008 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
8011 char response
[MAXLEN
];
8012 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
8013 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8016 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
8020 #else /* Use cool 8ball instead */
8022 void eightball(char *outcome
, int method
, unsigned int seed
)
8026 #define NUMOFCOLORS 18
8027 char ballcolors
[50][50] = {
8028 "blue", "red", "green", "yellow",
8029 "white", "black", "grey", "brown",
8030 "yellow", "pink", "purple", "orange", "teal", "burgandy",
8031 "fuchsia","turquoise","magenta", "cyan"};
8032 #define NUMOFLOCATIONS 50
8033 char balllocations
[50][55] = {
8034 "Locke's house", "Oregon", "California", "Indiana", "Canada",
8035 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
8036 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
8037 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
8038 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
8039 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
8040 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
8041 "your bra", "your hair", "your bed", "the couch", "the wall",
8042 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
8043 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
8044 #define NUMOFPREPS 15
8045 char ballpreps
[50][50] = {
8046 "Near", "Somewhere near", "In", "In", "In",
8047 "In", "Hiding in", "Under", "Next to", "Over",
8048 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
8049 #define NUMOFNUMS 34
8050 char ballnums
[50][50] = {
8051 "A hundred", "A thousand", "A few", "42",
8052 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
8053 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8054 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8056 #define NUMOFMULTS 8
8057 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
8060 * 0: normal (Not used in x3)
8067 if (method
== 1) /* A Color */
8071 answer
= (rand() % 12); /* Make sure this is the # of entries */
8074 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
8076 case 1: strcpy(tmp
, "Sort of a light %s color.");
8078 case 2: strcpy(tmp
, "Dark and dreary %s.");
8080 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
8082 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
8084 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
8086 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
8088 case 10: strcpy(tmp
, "Solid %s.");
8090 case 11: strcpy(tmp
, "Transparent %s.");
8092 default: strcpy(outcome
, "An invalid random number was generated.");
8095 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
8098 else if (method
== 2) /* Location */
8100 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
8102 else if (method
== 3) /* Number of ___ */
8104 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
8108 //Debug(DBGWARNING, "Error in 8ball.");
8113 static CHANSERV_FUNC(cmd_8ball
)
8115 char *word1
, *word2
, *word3
;
8116 static char eb
[MAXLEN
];
8117 unsigned int accum
, i
, j
;
8121 for(i
=1; i
<argc
; i
++)
8122 for(j
=0; argv
[i
][j
]; j
++)
8123 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
8125 accum
+= time(NULL
)/3600;
8127 word2
= argc
>2?argv
[2]:"";
8128 word3
= argc
>3?argv
[3]:"";
8131 if((word2
) && strcasecmp(word1
, "what") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8132 eightball(eb
, 1, accum
);
8133 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8134 eightball(eb
, 1, accum
);
8135 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && ((strcasecmp(word2
, "color") == 0) || (strcasecmp(word2
, "colour") == 0)))
8136 eightball(eb
, 1, accum
);
8137 /*** LOCATION *****/
8142 (strcasecmp(word1
, "where") == 0) &&
8143 (strcasecmp(word2
, "is") == 0)
8147 strcasecmp(word1
, "where's") == 0
8150 eightball(eb
, 2, accum
);
8152 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
8153 eightball(eb
, 3, accum
);
8157 /* Generic 8ball question.. so pull from x3.conf srvx style */
8160 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
8163 char response
[MAXLEN
];
8164 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
8165 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8168 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
8174 char response
[MAXLEN
];
8175 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
8176 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
8179 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
8184 static CHANSERV_FUNC(cmd_d
)
8186 unsigned long sides
, count
, modifier
, ii
, total
;
8187 char response
[MAXLEN
], *sep
;
8191 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
8201 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
8202 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
8206 else if((sep
[0] == '-') && isdigit(sep
[1]))
8207 modifier
= strtoul(sep
, NULL
, 10);
8208 else if((sep
[0] == '+') && isdigit(sep
[1]))
8209 modifier
= strtoul(sep
+1, NULL
, 10);
8216 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
8221 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
8224 for(total
= ii
= 0; ii
< count
; ++ii
)
8225 total
+= (rand() % sides
) + 1;
8228 if((count
> 1) || modifier
)
8230 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
8231 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
8235 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
8236 sprintf(response
, fmt
, total
, sides
);
8239 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8241 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8245 static CHANSERV_FUNC(cmd_huggle
)
8247 /* CTCP must be via PRIVMSG, never notice */
8249 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
8251 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
8255 static CHANSERV_FUNC(cmd_calc
)
8257 char response
[MAXLEN
];
8260 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8263 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
8265 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
8269 static CHANSERV_FUNC(cmd_reply
)
8273 unsplit_string(argv
+ 1, argc
- 1, NULL
);
8276 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
8278 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
8283 chanserv_adjust_limit(void *data
)
8285 struct mod_chanmode change
;
8286 struct chanData
*cData
= data
;
8287 struct chanNode
*channel
= cData
->channel
;
8290 if(IsSuspended(cData
))
8293 cData
->limitAdjusted
= now
;
8294 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
8295 if(cData
->modes
.modes_set
& MODE_LIMIT
)
8297 if(limit
> cData
->modes
.new_limit
)
8298 limit
= cData
->modes
.new_limit
;
8299 else if(limit
== cData
->modes
.new_limit
)
8303 mod_chanmode_init(&change
);
8304 change
.modes_set
= MODE_LIMIT
;
8305 change
.new_limit
= limit
;
8306 mod_chanmode_announce(chanserv
, channel
, &change
);
8310 handle_new_channel(struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8312 struct chanData
*cData
;
8314 if(!(cData
= channel
->channel_info
))
8317 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
8318 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
8320 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
8321 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
8325 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
8327 struct banData
*bData
;
8328 struct mod_chanmode
*change
;
8330 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
8335 if (chan
->channel_info
) {
8336 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
8338 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0))
8350 check_bans(struct userNode
*user
, const char *channel
)
8352 struct chanNode
*chan
;
8353 struct mod_chanmode change
;
8354 struct chanData
*cData
;
8355 struct banData
*bData
;
8357 if (!(chan
= GetChannel(channel
)))
8360 if(!(cData
= chan
->channel_info
))
8363 mod_chanmode_init(&change
);
8366 if(chan
->banlist
.used
< MAXBANS
)
8368 /* Not joining through a ban. */
8369 for(bData
= cData
->bans
;
8370 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8371 bData
= bData
->next
);
8375 char kick_reason
[MAXLEN
];
8376 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8378 bData
->triggered
= now
;
8379 if(bData
!= cData
->bans
)
8381 /* Shuffle the ban to the head of the list. */
8383 bData
->next
->prev
= bData
->prev
;
8385 bData
->prev
->next
= bData
->next
;
8388 bData
->next
= cData
->bans
;
8391 cData
->bans
->prev
= bData
;
8393 cData
->bans
= bData
;
8396 change
.args
[0].mode
= MODE_BAN
;
8397 change
.args
[0].u
.hostmask
= bData
->mask
;
8398 mod_chanmode_announce(chanserv
, chan
, &change
);
8399 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8407 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
8410 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
8412 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
, 0))
8419 /* Welcome to my worst nightmare. Warning: Read (or modify)
8420 the code below at your own risk. */
8422 handle_join(struct modeNode
*mNode
, UNUSED_ARG(void *extra
))
8424 struct mod_chanmode change
;
8425 struct userNode
*user
= mNode
->user
;
8426 struct chanNode
*channel
= mNode
->channel
;
8427 struct chanData
*cData
;
8428 struct userData
*uData
= NULL
;
8429 struct banData
*bData
;
8430 struct handle_info
*handle
;
8431 unsigned int modes
= 0, info
= 0;
8434 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8437 cData
= channel
->channel_info
;
8438 if(channel
->members
.used
> cData
->max
)
8439 cData
->max
= channel
->members
.used
;
8442 /* Check for bans. If they're joining through a ban, one of two
8444 * 1: Join during a netburst, by riding the break. Kick them
8445 * unless they have ops or voice in the channel.
8446 * 2: They're allowed to join through the ban (an invite in
8447 * ircu2.10, or a +e on Hybrid, or something).
8448 * If they're not joining through a ban, and the banlist is not
8449 * full, see if they're on the banlist for the channel. If so,
8452 if(user
->uplink
->burst
&& !mNode
->modes
)
8455 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
8457 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
, 0))
8459 /* Riding a netburst. Naughty. */
8460 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
8467 if(user
->handle_info
)
8469 handle
= user
->handle_info
;
8472 uData
= GetTrueChannelAccess(cData
, handle
);
8477 mod_chanmode_init(&change
);
8480 /* TODO: maybe only people above inviteme level? -Rubin */
8481 /* We don't kick people with access */
8482 if(!uData
&& !channel_user_is_exempt(user
, channel
))
8484 if(channel
->banlist
.used
< MAXBANS
)
8486 /* Not joining through a ban. */
8487 for(bData
= cData
->bans
;
8488 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
, 0);
8489 bData
= bData
->next
);
8493 char kick_reason
[MAXLEN
];
8494 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8496 bData
->triggered
= now
;
8497 if(bData
!= cData
->bans
)
8499 /* Shuffle the ban to the head of the list. */
8501 bData
->next
->prev
= bData
->prev
;
8503 bData
->prev
->next
= bData
->next
;
8506 bData
->next
= cData
->bans
;
8509 cData
->bans
->prev
= bData
;
8510 cData
->bans
= bData
;
8513 change
.args
[0].mode
= MODE_BAN
;
8514 change
.args
[0].u
.hostmask
= bData
->mask
;
8515 mod_chanmode_announce(chanserv
, channel
, &change
);
8516 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8522 /* ChanServ will not modify the limits in join-flooded channels.
8523 It will also skip DynLimit processing when the user (or srvx)
8524 is bursting in, because there are likely more incoming. */
8525 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8526 && !user
->uplink
->burst
8527 && !channel
->join_flooded
8528 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
8530 /* The user count has begun "bumping" into the channel limit,
8531 so set a timer to raise the limit a bit. Any previous
8532 timers are removed so three incoming users within the delay
8533 results in one limit change, not three. */
8535 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8536 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8539 /* Give automodes exept during join-floods */
8540 if(!channel
->join_flooded
)
8542 if(cData
->chOpts
[chAutomode
] == 'v')
8543 modes
|= MODE_VOICE
;
8544 else if(cData
->chOpts
[chAutomode
] == 'h')
8545 modes
|= MODE_HALFOP
;
8546 else if(cData
->chOpts
[chAutomode
] == 'o')
8547 modes
|= MODE_CHANOP
;
8550 greeting
= cData
->greeting
;
8551 if(user
->handle_info
)
8553 /* handle = user->handle_info; */
8555 if(IsHelper(user
) && !IsHelping(user
))
8558 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8560 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
8562 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8568 /* uData = GetTrueChannelAccess(cData, handle); */
8569 if(uData
&& !IsUserSuspended(uData
))
8571 /* non users getting automodes are handled above. */
8572 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
8574 /* just op everyone with access */
8575 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8576 modes
|= MODE_VOICE
;
8577 /* or do their access level */
8578 else if(uData
->access
>= UL_OP
)
8579 modes
|= MODE_CHANOP
;
8580 else if(uData
->access
>= UL_HALFOP
)
8581 modes
|= MODE_HALFOP
;
8582 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
8583 modes
|= MODE_VOICE
;
8585 if(uData
->access
>= UL_PRESENT
)
8586 cData
->visited
= now
;
8587 if(cData
->user_greeting
)
8588 greeting
= cData
->user_greeting
;
8590 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
8591 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
8599 /* If user joining normally (not during burst), apply op or voice,
8600 * and send greeting/userinfo as appropriate.
8602 if(!user
->uplink
->burst
)
8606 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8607 if(modes & MODE_CHANOP) {
8608 modes &= ~MODE_HALFOP;
8609 modes &= ~MODE_VOICE;
8612 change
.args
[0].mode
= modes
;
8613 change
.args
[0].u
.member
= mNode
;
8614 mod_chanmode_announce(chanserv
, channel
, &change
);
8617 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
8619 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
8625 chanserv_autojoin_channels(struct userNode
*user
)
8627 struct userData
*channel
;
8629 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8631 struct chanNode
*cn
;
8632 struct modeNode
*mn
;
8634 if(IsUserSuspended(channel
)
8635 || IsSuspended(channel
->channel
)
8636 || !(cn
= channel
->channel
->channel
))
8639 mn
= GetUserMode(cn
, user
);
8642 if(!IsUserSuspended(channel
)
8643 && IsUserAutoJoin(channel
)
8644 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8646 && !user
->uplink
->burst
)
8647 irc_svsjoin(chanserv
, user
, cn
);
8653 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
), UNUSED_ARG(void *extra
))
8655 struct mod_chanmode change
;
8656 struct userData
*channel
;
8657 unsigned int ii
, jj
, i
;
8659 if(!user
->handle_info
)
8662 mod_chanmode_init(&change
);
8664 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8666 struct chanNode
*cn
;
8667 struct chanData
*cData
;
8668 struct modeNode
*mn
;
8669 if(IsUserSuspended(channel
)
8670 || IsSuspended(channel
->channel
)
8671 || !(cn
= channel
->channel
->channel
))
8674 cData
= cn
->channel_info
;
8675 mn
= GetUserMode(cn
, user
);
8678 if(!IsUserSuspended(channel
)
8679 && IsUserAutoInvite(channel
)
8680 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8682 && !user
->uplink
->burst
)
8683 irc_invite(chanserv
, user
, cn
);
8687 if(channel
->access
>= UL_PRESENT
)
8688 channel
->channel
->visited
= now
;
8690 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8692 if (channel
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8693 change
.args
[0].mode
= MODE_VOICE
;
8694 else if(channel
->access
>= UL_OP
)
8695 change
.args
[0].mode
= MODE_CHANOP
;
8696 else if(channel
->access
>= UL_HALFOP
)
8697 change
.args
[0].mode
= MODE_HALFOP
;
8698 else if(channel
->access
>= UL_PEON
)
8699 change
.args
[0].mode
= MODE_VOICE
;
8701 change
.args
[0].mode
= 0;
8702 change
.args
[0].u
.member
= mn
;
8703 if(change
.args
[0].mode
)
8704 mod_chanmode_announce(chanserv
, cn
, &change
);
8707 channel
->seen
= now
;
8708 channel
->present
= 1;
8711 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8713 struct chanNode
*chan
= user
->channels
.list
[ii
]->channel
;
8714 struct banData
*ban
;
8716 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8717 || !chan
->channel_info
8718 || IsSuspended(chan
->channel_info
))
8720 if(protect_user(user
, chanserv
, chan
->channel_info
, true))
8722 for(jj
= 0; jj
< chan
->banlist
.used
; ++jj
)
8723 if(user_matches_glob(user
, chan
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8725 if(jj
< chan
->banlist
.used
)
8727 for(ban
= chan
->channel_info
->bans
; ban
; ban
= ban
->next
)
8729 char kick_reason
[MAXLEN
];
8730 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
, 0))
8732 change
.args
[0].mode
= MODE_BAN
;
8733 change
.args
[0].u
.hostmask
= ban
->mask
;
8734 mod_chanmode_announce(chanserv
, chan
, &change
);
8735 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8736 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
8737 ban
->triggered
= now
;
8742 if(IsSupportHelper(user
))
8744 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8746 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8748 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8754 if (user
->handle_info
->ignores
->used
) {
8755 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8756 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8760 if (user
->handle_info
->epithet
)
8761 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8763 /* process autojoin channels 5 seconds later as this sometimes
8764 happens before autohide */
8765 //timeq_add(now + 5, chanserv_autojoin_channels, user);
8766 chanserv_autojoin_channels(user
);
8770 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
), UNUSED_ARG(void *extra
))
8772 struct chanData
*cData
;
8773 struct userData
*uData
;
8775 cData
= mn
->channel
->channel_info
;
8776 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8779 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8781 /* Allow for a bit of padding so that the limit doesn't
8782 track the user count exactly, which could get annoying. */
8783 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8785 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8786 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8790 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8792 scan_user_presence(uData
, mn
->user
);
8794 if (uData
->access
>= UL_PRESENT
)
8795 cData
->visited
= now
;
8798 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8801 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8803 struct chanNode
*channel
;
8804 struct userNode
*exclude
;
8805 /* When looking at the channel that is being /part'ed, we
8806 * have to skip over the client that is leaving. For
8807 * other channels, we must not do that.
8809 channel
= chanserv_conf
.support_channels
.list
[ii
];
8810 exclude
= (channel
== mn
->channel
) ? mn
->user
: NULL
;
8811 if(find_handle_in_channel(channel
, mn
->user
->handle_info
, exclude
))
8814 if(ii
== chanserv_conf
.support_channels
.used
)
8815 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8820 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
, UNUSED_ARG(void *extra
))
8822 struct userData
*uData
;
8824 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8825 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8826 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8829 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8831 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8832 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8835 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8840 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
, UNUSED_ARG(void *extra
))
8842 struct chanData
*cData
;
8844 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8847 cData
= channel
->channel_info
;
8848 if(bad_topic(channel
, user
, channel
->topic
))
8849 { /* User doesnt have privs to set topics. Undo it */
8850 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8851 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8854 /* If there is a topic mask set, and the new topic doesnt match,
8855 * set the topic to mask + new_topic */
8856 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8858 char new_topic
[TOPICLEN
+1];
8859 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8862 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8863 /* and fall through to topicsnarf code below.. */
8865 else /* Topic couldnt fit into mask, was too long */
8867 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8868 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8869 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8873 /* With topicsnarf, grab the topic and save it as the default topic. */
8874 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8877 cData
->topic
= strdup(channel
->topic
);
8883 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
, UNUSED_ARG(void *extra
))
8885 struct mod_chanmode
*bounce
= NULL
;
8886 unsigned int bnc
, ii
;
8889 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8892 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8893 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8895 char correct
[MAXLEN
];
8896 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8897 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8898 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8900 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8902 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8904 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8905 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8908 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8911 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8912 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8913 if(bounce
->args
[bnc
].u
.member
)
8917 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8918 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8920 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8922 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8924 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8925 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8928 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8929 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8930 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8933 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8935 const char *ban
= change
->args
[ii
].u
.hostmask
;
8936 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8939 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8940 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8941 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8943 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8948 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8949 mod_chanmode_announce(chanserv
, channel
, bounce
);
8950 for(ii
= 0; ii
< change
->argc
; ++ii
)
8951 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8952 free((char*)bounce
->args
[ii
].u
.hostmask
);
8953 mod_chanmode_free(bounce
);
8958 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
), UNUSED_ARG(void *extra
))
8960 struct chanNode
*channel
;
8961 struct banData
*bData
;
8962 struct mod_chanmode change
;
8963 unsigned int ii
, jj
;
8964 char kick_reason
[MAXLEN
];
8966 mod_chanmode_init(&change
);
8968 change
.args
[0].mode
= MODE_BAN
;
8969 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8971 channel
= user
->channels
.list
[ii
]->channel
;
8972 /* Need not check for bans if they're opped or voiced. */
8973 /* TODO: does this make sense in automode v, h, and o? *
8974 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8975 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8977 /* Need not check for bans unless channel registration is active. */
8978 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8980 /* Look for a matching ban already on the channel. */
8981 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8982 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
, 0))
8984 /* Need not act if we found one. */
8985 if(jj
< channel
->banlist
.used
)
8987 /* don't kick someone on the userlist */
8988 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8990 /* Look for a matching ban in this channel. */
8991 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8993 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
, 0))
8995 change
.args
[0].u
.hostmask
= bData
->mask
;
8996 mod_chanmode_announce(chanserv
, channel
, &change
);
8997 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8998 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8999 bData
->triggered
= now
;
9000 break; /* we don't need to check any more bans in the channel */
9005 static void handle_rename(struct handle_info
*handle
, const char *old_handle
, UNUSED_ARG(void *extra
))
9007 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
9011 dict_remove2(handle_dnrs
, old_handle
, 1);
9012 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
9013 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
9018 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
, UNUSED_ARG(void *extra
))
9020 struct userNode
*h_user
;
9022 if(handle
->channels
)
9024 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
9025 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
9027 while(handle
->channels
)
9028 del_channel_user(handle
->channels
, 1);
9033 handle_server_link(UNUSED_ARG(struct server
*server
), UNUSED_ARG(void *extra
))
9035 struct chanData
*cData
;
9037 for(cData
= channelList
; cData
; cData
= cData
->next
)
9039 if(!IsSuspended(cData
))
9040 cData
->may_opchan
= 1;
9041 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
9042 && !cData
->channel
->join_flooded
9043 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
9044 < chanserv_conf
.adjust_threshold
))
9046 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
9047 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
9054 chanserv_conf_read(void)
9058 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
9059 struct mod_chanmode
*change
;
9060 struct string_list
*strlist
;
9061 struct chanNode
*chan
;
9064 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
9066 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
9069 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9070 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9071 chanserv_conf
.support_channels
.used
= 0;
9072 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
9074 for(ii
= 0; ii
< strlist
->used
; ++ii
)
9076 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9079 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
9081 channelList_append(&chanserv_conf
.support_channels
, chan
);
9084 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
9087 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
9090 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
9092 channelList_append(&chanserv_conf
.support_channels
, chan
);
9094 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
9095 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
9096 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
9097 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
9098 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
9099 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
9100 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
9101 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
9102 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
9103 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
9104 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
9105 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
9106 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
9107 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
9108 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
9109 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
9110 str
= database_get_data(conf_node
, KEY_DNR_EXPIRE_FREQ
, RECDB_QSTRING
);
9111 chanserv_conf
.dnr_expire_frequency
= str
? ParseInterval(str
) : 3600;
9112 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
9113 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
9114 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
9115 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
9116 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
9117 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
9118 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
9119 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
9120 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
9122 NickChange(chanserv
, str
, 0);
9123 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
9124 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
9125 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
9126 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
9127 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
9128 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
9129 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
9130 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
9131 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
9132 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
9133 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
9134 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
9135 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
9136 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
9137 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
9138 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
9139 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
9140 god_timeout
= str
? ParseInterval(str
) : 60*15;
9141 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
9144 safestrncpy(mode_line
, str
, sizeof(mode_line
));
9145 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
9146 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
9147 && (change
->argc
< 2))
9149 chanserv_conf
.default_modes
= *change
;
9150 mod_chanmode_free(change
);
9152 str
= database_get_data(conf_node
, KEY_VALID_CHANNEL_REGEX
, RECDB_QSTRING
);
9153 if (chanserv_conf
.valid_channel_regex_set
)
9154 regfree(&chanserv_conf
.valid_channel_regex
);
9156 int err
= regcomp(&chanserv_conf
.valid_channel_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
9157 chanserv_conf
.valid_channel_regex_set
= !err
;
9158 if (err
) log_module(CS_LOG
, LOG_ERROR
, "Bad valid_channel_regex (error %d)", err
);
9161 chanserv_conf
.valid_channel_regex_set
= 0;
9163 free_string_list(chanserv_conf
.wheel
);
9164 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
9166 strlist
= string_list_copy(strlist
);
9169 static const char *list
[] = {
9170 "peer", "partall", "gline", /* "shun", */
9171 "nothing", "randjoin", "abusewhois", "kickall",
9172 "nickchange", "kill", "svsignore", "kickbanall",
9175 strlist
= alloc_string_list(ArrayLength(list
)-1);
9176 for(ii
=0; list
[ii
]; ii
++)
9177 string_list_append(strlist
, strdup(list
[ii
]));
9179 chanserv_conf
.wheel
= strlist
;
9181 free_string_list(chanserv_conf
.set_shows
);
9182 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
9184 strlist
= string_list_copy(strlist
);
9187 static const char *list
[] = {
9188 /* free form text */
9189 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9190 /* options based on user level */
9191 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9192 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
9193 /* multiple choice options */
9194 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
9195 /* binary options */
9196 "DynLimit", "NoDelete", "BanTimeout",
9200 strlist
= alloc_string_list(ArrayLength(list
)-1);
9201 for(ii
=0; list
[ii
]; ii
++)
9202 string_list_append(strlist
, strdup(list
[ii
]));
9204 chanserv_conf
.set_shows
= strlist
;
9205 /* We don't look things up now, in case the list refers to options
9206 * defined by modules initialized after this point. Just mark the
9207 * function list as invalid, so it will be initialized.
9209 set_shows_list
.used
= 0;
9211 free_string_list(chanserv_conf
.eightball
);
9212 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
9215 strlist
= string_list_copy(strlist
);
9219 strlist
= alloc_string_list(4);
9220 string_list_append(strlist
, strdup("Yes."));
9221 string_list_append(strlist
, strdup("No."));
9222 string_list_append(strlist
, strdup("Maybe so."));
9224 chanserv_conf
.eightball
= strlist
;
9226 free_string_list(chanserv_conf
.old_ban_names
);
9227 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
9229 strlist
= string_list_copy(strlist
);
9231 strlist
= alloc_string_list(2);
9232 chanserv_conf
.old_ban_names
= strlist
;
9233 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
9234 off_channel
= str
? atoi(str
) : 0;
9238 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
9241 struct note_type
*ntype
;
9244 if(!(obj
= GET_RECORD_OBJECT(rd
)))
9246 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
9249 if(!(ntype
= chanserv_create_note_type(key
)))
9251 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
9255 /* Figure out set access */
9256 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
9258 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9259 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
9261 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
9263 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
9264 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
9266 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
9268 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
9272 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
9273 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
9274 ntype
->set_access
.min_opserv
= 0;
9277 /* Figure out visibility */
9278 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
9279 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9280 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
9281 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9282 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
9283 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
9284 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
9285 ntype
->visible_type
= NOTE_VIS_ALL
;
9287 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
9289 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
9290 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
9294 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9296 struct handle_info
*handle
;
9297 struct userData
*uData
;
9298 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
9300 unsigned short access_level
, lastaccess
= 0;
9302 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9304 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
9308 access_level
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
9309 if(access_level
> UL_OWNER
)
9311 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
9315 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
9316 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
9317 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
9318 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
9319 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9320 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
9321 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
9322 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
9323 lastaccess
= lstacc
? atoi(lstacc
) : 0;
9325 handle
= get_handle_info(key
);
9328 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
9332 uData
= add_channel_user(chan
, handle
, access_level
, last_seen
, inf
, 0);
9333 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
9334 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
9336 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
9337 if (uData
->accessexpiry
> 0)
9338 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
9340 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
9341 if (uData
->clvlexpiry
> 0)
9342 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
9344 uData
->lastaccess
= lastaccess
;
9346 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
9348 if(uData
->expires
> now
)
9349 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
9351 uData
->flags
&= ~USER_SUSPENDED
;
9354 /* Upgrade: set autoop to the inverse of noautoop */
9355 if(chanserv_read_version
< 2)
9357 /* if noautoop is true, set autoop false, and vice versa */
9358 if(uData
->flags
& USER_NOAUTO_OP
)
9359 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
9361 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
9362 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
);
9368 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
9370 //struct banData *bData;
9371 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
9372 time_t set_time
, triggered_time
, expires_time
;
9374 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
9376 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
9380 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
9381 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
9382 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
9383 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9384 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
9385 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
9386 if (!reason
|| !owner
)
9389 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
9390 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
9392 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
9394 expires_time
= set_time
+ atoi(s_duration
);
9398 if(!reason
|| (expires_time
&& (expires_time
< now
)))
9401 add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
9404 static struct suspended
*
9405 chanserv_read_suspended(dict_t obj
)
9407 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
9411 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
9412 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9413 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
9414 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9415 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9416 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9417 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
9418 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
9419 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9420 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
9424 static struct giveownership
*
9425 chanserv_read_giveownership(dict_t obj
)
9427 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
9431 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
9432 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
9434 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
9436 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
9437 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
9439 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
9440 giveownership
->reason
= str
? strdup(str
) : NULL
;
9441 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
9442 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9444 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
9445 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
9446 return giveownership
;
9450 chanserv_channel_read(const char *key
, struct record_data
*hir
)
9452 struct suspended
*suspended
;
9453 struct giveownership
*giveownership
;
9454 struct mod_chanmode
*modes
;
9455 struct chanNode
*cNode
;
9456 struct chanData
*cData
;
9457 struct dict
*channel
, *obj
;
9458 char *str
, *argv
[10];
9462 channel
= hir
->d
.object
;
9464 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
9467 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
9470 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
9473 cData
= register_channel(cNode
, str
);
9476 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
9480 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
9482 enum levelOption lvlOpt
;
9483 enum charOption chOpt
;
9485 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
9486 cData
->flags
= atoi(str
);
9488 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9490 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
9492 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
9493 else if(levelOptions
[lvlOpt
].old_flag
)
9495 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9496 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
9498 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
9502 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9504 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
9506 cData
->chOpts
[chOpt
] = str
[0];
9509 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
9511 enum levelOption lvlOpt
;
9512 enum charOption chOpt
;
9515 cData
->flags
= base64toint(str
, 5);
9516 count
= strlen(str
+= 5);
9517 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9520 if(levelOptions
[lvlOpt
].old_flag
)
9522 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
9523 lvl
= levelOptions
[lvlOpt
].flag_value
;
9525 lvl
= levelOptions
[lvlOpt
].default_value
;
9527 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
9529 case 'c': lvl
= UL_COOWNER
; break;
9530 case 'm': lvl
= UL_MANAGER
; break;
9531 case 'n': lvl
= UL_OWNER
+1; break;
9532 case 'o': lvl
= UL_OP
; break;
9533 case 'p': lvl
= UL_PEON
; break;
9534 case 'h': lvl
= UL_HALFOP
; break;
9535 case 'w': lvl
= UL_OWNER
; break;
9536 default: lvl
= 0; break;
9538 cData
->lvlOpts
[lvlOpt
] = lvl
;
9540 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9541 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
9544 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
9546 suspended
= chanserv_read_suspended(obj
);
9547 cData
->suspended
= suspended
;
9548 suspended
->cData
= cData
;
9549 /* We could use suspended->expires and suspended->revoked to
9550 * set the CHANNEL_SUSPENDED flag, but we don't. */
9552 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
9554 suspended
= calloc(1, sizeof(*suspended
));
9555 suspended
->issued
= 0;
9556 suspended
->revoked
= 0;
9557 suspended
->suspender
= strdup(str
);
9558 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
9559 suspended
->expires
= str
? atoi(str
) : 0;
9560 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
9561 suspended
->reason
= strdup(str
? str
: "No reason");
9562 suspended
->previous
= NULL
;
9563 cData
->suspended
= suspended
;
9564 suspended
->cData
= cData
;
9568 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9569 suspended
= NULL
; /* to squelch a warning */
9572 if(IsSuspended(cData
)) {
9573 if(suspended
->expires
> now
)
9574 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
9575 else if(suspended
->expires
)
9576 cData
->flags
&= ~CHANNEL_SUSPENDED
;
9579 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
9581 giveownership
= chanserv_read_giveownership(obj
);
9582 cData
->giveownership
= giveownership
;
9585 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
9586 struct mod_chanmode change
;
9587 mod_chanmode_init(&change
);
9589 change
.args
[0].mode
= MODE_CHANOP
;
9590 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
9591 mod_chanmode_announce(chanserv
, cNode
, &change
);
9594 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
9595 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9596 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
9597 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
9598 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
9599 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9600 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
9601 cData
->max
= str
? atoi(str
) : 0;
9602 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
9603 cData
->greeting
= str
? strdup(str
) : NULL
;
9604 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
9605 cData
->user_greeting
= str
? strdup(str
) : NULL
;
9606 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
9607 cData
->topic_mask
= str
? strdup(str
) : NULL
;
9608 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
9609 cData
->topic
= str
? strdup(str
) : NULL
;
9611 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
9612 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
9614 if(!IsSuspended(cData
)
9615 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
9616 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
9617 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0)))
9619 cData
->modes
= *modes
;
9621 cData
->modes
.modes_set
|= MODE_REGISTERED
;
9622 if(cData
->modes
.argc
> 1)
9623 cData
->modes
.argc
= 1;
9624 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
9625 mod_chanmode_free(modes
);
9628 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
9629 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9630 user_read_helper(iter_key(it
), iter_data(it
), cData
);
9632 if(!cData
->users
&& !IsProtected(cData
))
9634 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
9635 unregister_channel(cData
, "has empty user list.");
9639 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
9640 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9641 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
9643 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
9644 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9646 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
9647 struct record_data
*rd
= iter_data(it
);
9648 const char *note
, *setter
;
9650 if(rd
->type
!= RECDB_OBJECT
)
9652 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9656 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9658 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9660 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9664 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9665 if(!setter
) setter
= "<unknown>";
9666 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9674 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9676 const char *setter
, *reason
, *str
;
9677 struct do_not_register
*dnr
;
9680 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9683 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9686 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9689 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9692 str
= database_get_data(hir
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9693 expiry
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9694 if(expiry
&& expiry
<= now
)
9696 dnr
= chanserv_add_dnr(key
, setter
, expiry
, reason
);
9699 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9701 dnr
->set
= atoi(str
);
9707 chanserv_version_read(struct dict
*section
)
9711 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9713 chanserv_read_version
= atoi(str
);
9714 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9718 chanserv_saxdb_read(struct dict
*database
)
9720 struct dict
*section
;
9723 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9724 chanserv_version_read(section
);
9726 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9727 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9728 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9730 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9731 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9732 chanserv_channel_read(iter_key(it
), iter_data(it
));
9734 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9735 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9736 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9742 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9744 int high_present
= 0;
9745 saxdb_start_record(ctx
, KEY_USERS
, 1);
9746 for(; uData
; uData
= uData
->next
)
9748 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9750 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9751 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9752 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9753 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9754 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9755 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9757 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9759 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9761 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9762 saxdb_end_record(ctx
);
9764 saxdb_end_record(ctx
);
9765 return high_present
;
9769 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9773 saxdb_start_record(ctx
, KEY_BANS
, 1);
9774 for(; bData
; bData
= bData
->next
)
9776 saxdb_start_record(ctx
, bData
->mask
, 0);
9777 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9778 if(bData
->triggered
)
9779 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9781 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9783 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9785 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9786 saxdb_end_record(ctx
);
9788 saxdb_end_record(ctx
);
9792 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9794 saxdb_start_record(ctx
, name
, 0);
9795 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9796 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9798 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9800 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9802 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9804 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9805 saxdb_end_record(ctx
);
9809 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9811 saxdb_start_record(ctx
, name
, 0);
9812 if(giveownership
->staff_issuer
)
9813 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9814 if(giveownership
->old_owner
)
9815 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9816 if(giveownership
->target
)
9817 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9818 if(giveownership
->target_access
)
9819 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9820 if(giveownership
->reason
)
9821 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9822 if(giveownership
->issued
)
9823 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9824 if(giveownership
->previous
)
9825 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9826 saxdb_end_record(ctx
);
9830 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9834 enum levelOption lvlOpt
;
9835 enum charOption chOpt
;
9837 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9839 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9840 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9842 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9843 if(channel
->registrar
)
9844 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9845 if(channel
->greeting
)
9846 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9847 if(channel
->user_greeting
)
9848 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9849 if(channel
->topic_mask
)
9850 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9851 if(channel
->suspended
)
9852 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9853 if(channel
->giveownership
)
9854 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9856 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9857 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9858 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9859 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9860 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9862 buf
[0] = channel
->chOpts
[chOpt
];
9864 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9866 saxdb_end_record(ctx
);
9868 if (channel
->maxsetinfo
)
9869 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9871 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9873 mod_chanmode_format(&channel
->modes
, buf
);
9874 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9877 high_present
= chanserv_write_users(ctx
, channel
->users
);
9878 chanserv_write_bans(ctx
, channel
->bans
);
9880 if(dict_size(channel
->notes
))
9884 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9885 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9887 struct note
*note
= iter_data(it
);
9888 saxdb_start_record(ctx
, iter_key(it
), 0);
9889 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9890 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9891 saxdb_end_record(ctx
);
9893 saxdb_end_record(ctx
);
9896 if(channel
->ownerTransfer
)
9897 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9898 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9899 saxdb_end_record(ctx
);
9903 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9907 saxdb_start_record(ctx
, ntype
->name
, 0);
9908 switch(ntype
->set_access_type
)
9910 case NOTE_SET_CHANNEL_ACCESS
:
9911 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9913 case NOTE_SET_CHANNEL_SETTER
:
9914 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9916 case NOTE_SET_PRIVILEGED
: default:
9917 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9920 switch(ntype
->visible_type
)
9922 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9923 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9924 case NOTE_VIS_PRIVILEGED
:
9925 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9927 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9928 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9929 saxdb_end_record(ctx
);
9933 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9935 struct do_not_register
*dnr
;
9936 dict_iterator_t it
, next
;
9938 for(it
= dict_first(dnrs
); it
; it
= next
)
9940 next
= iter_next(it
);
9941 dnr
= iter_data(it
);
9942 if(dnr
->expires
&& dnr
->expires
<= now
)
9944 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9946 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9949 dict_remove(dnrs
, iter_key(it
));
9950 saxdb_write_int(ctx
, KEY_EXPIRES
, dnr
->expires
);
9952 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9953 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9954 saxdb_end_record(ctx
);
9959 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9962 struct chanData
*channel
;
9964 /* Version Control*/
9965 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9966 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9967 saxdb_end_record(ctx
);
9970 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9971 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9972 chanserv_write_note_type(ctx
, iter_data(it
));
9973 saxdb_end_record(ctx
);
9976 saxdb_start_record(ctx
, KEY_DNR
, 1);
9977 write_dnrs_helper(ctx
, handle_dnrs
);
9978 write_dnrs_helper(ctx
, plain_dnrs
);
9979 write_dnrs_helper(ctx
, mask_dnrs
);
9980 saxdb_end_record(ctx
);
9983 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9984 for(channel
= channelList
; channel
; channel
= channel
->next
)
9985 chanserv_write_channel(ctx
, channel
);
9986 saxdb_end_record(ctx
);
9992 chanserv_db_cleanup(UNUSED_ARG(void *extra
)) {
9994 unreg_part_func(handle_part
, NULL
);
9996 unregister_channel(channelList
, "terminating.");
9997 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9998 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9999 free(chanserv_conf
.support_channels
.list
);
10000 dict_delete(handle_dnrs
);
10001 dict_delete(plain_dnrs
);
10002 dict_delete(mask_dnrs
);
10003 dict_delete(note_types
);
10004 free_string_list(chanserv_conf
.eightball
);
10005 free_string_list(chanserv_conf
.old_ban_names
);
10006 free_string_list(chanserv_conf
.wheel
);
10007 free_string_list(chanserv_conf
.set_shows
);
10008 free(set_shows_list
.list
);
10009 free(uset_shows_list
.list
);
10012 struct userData
*helper
= helperList
;
10013 helperList
= helperList
->next
;
10018 #if defined(GCC_VARMACROS)
10019 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
10020 #elif defined(C99_VARMACROS)
10021 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
10023 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
10024 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
10027 init_chanserv(const char *nick
)
10029 struct chanNode
*chan
;
10032 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
10033 conf_register_reload(chanserv_conf_read
);
10036 reg_server_link_func(handle_server_link
, NULL
);
10037 reg_new_channel_func(handle_new_channel
, NULL
);
10038 reg_join_func(handle_join
, NULL
);
10039 reg_part_func(handle_part
, NULL
);
10040 reg_kick_func(handle_kick
, NULL
);
10041 reg_topic_func(handle_topic
, NULL
);
10042 reg_mode_change_func(handle_mode
, NULL
);
10043 reg_nick_change_func(handle_nick_change
, NULL
);
10044 reg_auth_func(handle_auth
, NULL
);
10047 reg_handle_rename_func(handle_rename
, NULL
);
10048 reg_unreg_func(handle_unreg
, NULL
);
10050 handle_dnrs
= dict_new();
10051 dict_set_free_data(handle_dnrs
, free
);
10052 plain_dnrs
= dict_new();
10053 dict_set_free_data(plain_dnrs
, free
);
10054 mask_dnrs
= dict_new();
10055 dict_set_free_data(mask_dnrs
, free
);
10057 reg_svccmd_unbind_func(handle_svccmd_unbind
, NULL
);
10058 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
10059 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+channel", NULL
);
10060 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
10061 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
10062 DEFINE_COMMAND(dnrsearch
, 3, 0, "template", "noregister", NULL
);
10063 modcmd_register(chanserv_module
, "dnrsearch print", NULL
, 0, 0, NULL
);
10064 modcmd_register(chanserv_module
, "dnrsearch remove", NULL
, 0, 0, NULL
);
10065 modcmd_register(chanserv_module
, "dnrsearch count", NULL
, 0, 0, NULL
);
10066 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
10067 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
|MODCMD_IGNORE_CSUSPEND
, "flags", "+helping", NULL
);
10068 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
|MODCMD_IGNORE_CSUSPEND
, "flags", "+helping", NULL
);
10069 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
10070 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
10072 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
10074 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
10075 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
10077 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10078 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10079 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10080 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10081 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10083 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
10084 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
10085 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
10086 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10087 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10088 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10089 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10091 DEFINE_COMMAND(levels
, 1, 0, NULL
);
10093 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10094 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
10095 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10096 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
10098 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
10099 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
10100 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10101 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10102 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10103 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
10104 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10105 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10106 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10107 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
10109 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10110 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10111 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
10112 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
10113 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
10114 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
10115 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
10116 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
10117 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
10118 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
10119 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
10120 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
10121 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10122 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
10124 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10125 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
10126 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10127 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10129 /* if you change dellamer access, see also places
10130 * like unbanme which have manager hardcoded. */
10131 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
10132 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
10134 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
10136 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
10138 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
10139 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10140 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10141 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10142 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10143 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10144 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10145 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10146 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10147 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10148 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10149 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
10151 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
10152 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
10154 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
10155 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
10156 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
10157 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
10159 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10160 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
10161 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
10162 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
10163 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
10165 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10166 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10167 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10168 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10169 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10170 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10171 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10172 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10173 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10174 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
10175 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
10177 /* Channel options */
10178 DEFINE_CHANNEL_OPTION(defaulttopic
);
10179 DEFINE_CHANNEL_OPTION(topicmask
);
10180 DEFINE_CHANNEL_OPTION(greeting
);
10181 DEFINE_CHANNEL_OPTION(usergreeting
);
10182 DEFINE_CHANNEL_OPTION(modes
);
10183 DEFINE_CHANNEL_OPTION(enfops
);
10184 DEFINE_CHANNEL_OPTION(enfhalfops
);
10185 DEFINE_CHANNEL_OPTION(automode
);
10186 DEFINE_CHANNEL_OPTION(protect
);
10187 DEFINE_CHANNEL_OPTION(enfmodes
);
10188 DEFINE_CHANNEL_OPTION(enftopic
);
10189 DEFINE_CHANNEL_OPTION(pubcmd
);
10190 DEFINE_CHANNEL_OPTION(userinfo
);
10191 DEFINE_CHANNEL_OPTION(dynlimit
);
10192 DEFINE_CHANNEL_OPTION(topicsnarf
);
10193 DEFINE_CHANNEL_OPTION(nodelete
);
10194 DEFINE_CHANNEL_OPTION(toys
);
10195 DEFINE_CHANNEL_OPTION(setters
);
10196 DEFINE_CHANNEL_OPTION(topicrefresh
);
10197 DEFINE_CHANNEL_OPTION(resync
);
10198 DEFINE_CHANNEL_OPTION(ctcpreaction
);
10199 DEFINE_CHANNEL_OPTION(bantimeout
);
10200 DEFINE_CHANNEL_OPTION(inviteme
);
10201 DEFINE_CHANNEL_OPTION(unreviewed
);
10202 modcmd_register(chanserv_module
, "set unreviewed on", NULL
, 0, 0, "flags", "+helping", NULL
);
10203 modcmd_register(chanserv_module
, "set unreviewed off", NULL
, 0, 0, "flags", "+oper", NULL
);
10204 DEFINE_CHANNEL_OPTION(maxsetinfo
);
10205 if(off_channel
> 1)
10206 DEFINE_CHANNEL_OPTION(offchannel
);
10207 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
10209 /* Alias set topic to set defaulttopic for compatibility. */
10210 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
10213 DEFINE_USER_OPTION(autoinvite
);
10214 DEFINE_USER_OPTION(autojoin
);
10215 DEFINE_USER_OPTION(info
);
10216 DEFINE_USER_OPTION(autoop
);
10218 /* Alias uset autovoice to uset autoop. */
10219 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
10221 note_types
= dict_new();
10222 dict_set_free_data(note_types
, chanserv_deref_note_type
);
10225 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
10226 chanserv
= AddLocalUser(nick
, nick
, NULL
, "Channel Services", modes
);
10227 service_register(chanserv
)->trigger
= '!';
10228 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
, NULL
);
10231 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
10233 if(chanserv_conf
.channel_expire_frequency
)
10234 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
10236 if(chanserv_conf
.dnr_expire_frequency
)
10237 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
10239 if(chanserv_conf
.ban_timeout_frequency
)
10240 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
10242 if(chanserv_conf
.refresh_period
)
10244 time_t next_refresh
;
10245 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
10246 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
10247 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
10250 if (autojoin_channels
&& chanserv
) {
10251 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
10252 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
10253 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
10257 reg_exit_func(chanserv_db_cleanup
, NULL
);
10258 message_register_table(msgtab
);