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() */
33 #define CHANSERV_CONF_NAME "services/chanserv"
35 /* ChanServ options */
36 #define KEY_SUPPORT_CHANNEL "support_channel"
37 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
38 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
39 #define KEY_INFO_DELAY "info_delay"
40 #define KEY_MAX_GREETLEN "max_greetlen"
41 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
42 #define KEY_ADJUST_DELAY "adjust_delay"
43 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
44 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
45 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
46 #define KEY_MAX_CHAN_USERS "max_chan_users"
47 #define KEY_MAX_CHAN_BANS "max_chan_bans"
48 #define KEY_NICK "nick"
49 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
50 #define KEY_8BALL_RESPONSES "8ball"
51 #define KEY_OLD_BAN_NAMES "old_ban_names"
52 #define KEY_REFRESH_PERIOD "refresh_period"
53 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
54 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
55 #define KEY_MAX_OWNED "max_owned"
56 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
57 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
58 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
59 #define KEY_NODELETE_LEVEL "nodelete_level"
60 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
61 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
62 #define KEY_VALID_CHANNEL_REGEX "valid_channel_regex"
64 /* ChanServ database */
65 #define KEY_VERSION_CONTROL "version_control"
66 #define KEY_CHANNELS "channels"
67 #define KEY_NOTE_TYPES "note_types"
69 /* version control paramiter */
70 #define KEY_VERSION_NUMBER "version_number"
72 /* Note type parameters */
73 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
74 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
75 #define KEY_NOTE_SETTER_ACCESS "setter_access"
76 #define KEY_NOTE_VISIBILITY "visibility"
77 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
78 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
79 #define KEY_NOTE_VIS_ALL "all"
80 #define KEY_NOTE_MAX_LENGTH "max_length"
81 #define KEY_NOTE_SETTER "setter"
82 #define KEY_NOTE_NOTE "note"
84 /* Do-not-register channels */
86 #define KEY_DNR_SET "set"
87 #define KEY_DNR_SETTER "setter"
88 #define KEY_DNR_REASON "reason"
91 #define KEY_REGISTERED "registered"
92 #define KEY_REGISTRAR "registrar"
93 #define KEY_SUSPENDED "suspended"
94 #define KEY_PREVIOUS "previous"
95 #define KEY_SUSPENDER "suspender"
96 #define KEY_ISSUED "issued"
97 #define KEY_REVOKED "revoked"
98 #define KEY_SUSPEND_EXPIRES "suspend_expires"
99 #define KEY_SUSPEND_REASON "suspend_reason"
100 #define KEY_GIVEOWNERSHIP "giveownership"
101 #define KEY_STAFF_ISSUER "staff_issuer"
102 #define KEY_OLD_OWNER "old_owner"
103 #define KEY_TARGET "target"
104 #define KEY_TARGET_ACCESS "target_access"
105 #define KEY_VISITED "visited"
106 #define KEY_TOPIC "topic"
107 #define KEY_GREETING "greeting"
108 #define KEY_USER_GREETING "user_greeting"
109 #define KEY_MODES "modes"
110 #define KEY_FLAGS "flags"
111 #define KEY_OPTIONS "options"
112 #define KEY_USERS "users"
113 #define KEY_BANS "bans" /* for lamers */
114 #define KEY_MAX "max"
115 #define KEY_NOTES "notes"
116 #define KEY_TOPIC_MASK "topic_mask"
117 #define KEY_OWNER_TRANSFER "owner_transfer"
118 #define KEY_MAXSETINFO "maxsetinfo"
121 #define KEY_LEVEL "level"
122 #define KEY_INFO "info"
123 #define KEY_SEEN "seen"
124 #define KEY_ACCESSEXPIRY "accessexpiry"
125 #define KEY_CLVLEXPIRY "clvlexpiry"
126 #define KEY_LASTLEVEL "lastlevel"
129 #define KEY_OWNER "owner"
130 #define KEY_REASON "reason"
131 #define KEY_SET "set"
132 #define KEY_DURATION "duration"
133 #define KEY_EXPIRES "expires"
134 #define KEY_TRIGGERED "triggered"
136 #define KEY_GOD_TIMEOUT "god_timeout"
138 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
139 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
141 /* Administrative messages */
142 static const struct message_entry msgtab
[] = {
143 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
145 /* Channel registration */
146 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
147 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
148 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
149 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
150 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
151 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
152 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
153 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
155 /* Do-not-register channels */
156 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
157 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
158 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
159 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
160 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
161 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
162 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
163 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
164 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
165 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
166 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
168 /* Channel unregistration */
169 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
170 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
171 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
172 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
175 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
176 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
178 /* Channel merging */
179 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
180 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
181 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
182 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
183 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
185 /* Handle unregistration */
186 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
189 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
190 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
191 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
192 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
193 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
194 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
195 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
196 { "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." },
197 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
198 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
199 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
200 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
201 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
202 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
203 { "CSMSG_NOT_IN_CHANNEL", "I am not in %s." },
205 /* Removing yourself from a channel. */
206 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
207 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
208 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
210 /* User management */
211 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
212 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
213 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
214 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
215 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
216 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
217 { "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." },
218 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
219 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
220 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
221 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
222 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
223 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
224 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
225 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
226 { "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" },
227 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
229 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
230 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
231 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
232 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
233 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
234 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
235 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
236 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
239 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
240 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
241 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
242 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
243 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
244 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
245 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
246 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
247 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
248 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
249 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
250 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
251 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
252 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
253 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
254 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
255 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
257 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
259 /* Channel management */
260 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
261 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
262 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
264 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
265 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
266 { "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" },
267 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
268 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
269 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
270 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
272 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
273 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
274 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
275 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
276 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
277 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
278 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
279 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
280 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
281 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
282 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
283 { "CSMSG_INVALID_NUMERIC", "$b%s$b is not a valid choice. Choose one:" },
284 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
285 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
286 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
287 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
288 { "CSMSG_SET_MODES", "$bModes $b %s" },
289 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
290 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
291 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
292 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
293 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
294 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
295 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
296 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
297 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
298 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
299 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
300 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
301 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
302 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
303 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
304 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
305 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
306 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
307 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
308 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
310 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
311 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
312 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
313 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
314 { "CSMSG_USET_INFO", "$bInfo $b %s" },
316 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
317 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
318 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
319 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
320 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
321 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
322 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
323 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
324 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
325 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
326 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
328 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
329 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
330 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
331 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
332 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
333 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
334 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
336 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
337 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
338 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
339 { "CSMSG_PROTECT_NONE", "No users will be protected." },
340 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
341 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
342 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
344 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
345 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
346 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
347 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
348 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
350 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
351 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
352 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
353 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
354 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
356 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
357 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
358 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
359 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
360 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
362 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
363 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
364 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
365 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
366 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
367 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
369 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
370 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
371 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
372 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
373 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
374 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
375 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
376 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
377 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
379 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
380 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
381 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
383 /* Channel userlist */
384 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
385 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
386 /* uncomment if needed to adujust styles (and change code below)
387 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
388 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
389 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
390 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
391 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
392 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
394 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
395 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
396 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
398 /* Channel note list */
399 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
400 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
401 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
402 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
403 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
404 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
405 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
406 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
407 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
408 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
409 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
410 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
411 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
412 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
413 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
415 /* Channel [un]suspension */
416 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
417 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
418 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
419 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
420 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
421 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
422 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
424 /* Access information */
425 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
426 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
427 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
428 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
429 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
430 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
431 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
432 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
433 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
434 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
435 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
437 /* Seen information */
438 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
439 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
440 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
441 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
443 /* Names information */
444 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
445 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
447 /* Channel information */
448 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
449 { "CSMSG_BAR", "----------------------------------------"},
450 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
451 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
452 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
453 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
454 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
455 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
456 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
457 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
458 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
459 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
460 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
461 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
462 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
463 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
464 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
465 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
466 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
467 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
468 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
469 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
470 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
471 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
472 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
473 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
474 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
475 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
477 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
478 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
479 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
480 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
481 { "CSMSG_PEEK_OPS", "$bOps:$b" },
482 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
483 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
485 /* Network information */
486 { "CSMSG_NETWORK_INFO", "Network Information:" },
487 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
488 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
489 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
490 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
491 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
492 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
493 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
494 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
497 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
498 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
499 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
501 /* Channel searches */
502 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
503 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
504 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
505 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
507 /* Channel configuration */
508 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
509 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
510 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
511 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
514 { "CSMSG_USER_OPTIONS", "User Options:" },
515 // { "CSMSG_USER_PROTECTED", "That user is protected." },
518 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
519 { "CSMSG_PING_RESPONSE", "Pong!" },
520 { "CSMSG_WUT_RESPONSE", "wut" },
521 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
522 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
523 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
524 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
525 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
526 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
527 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
528 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
529 { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
530 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
531 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
532 { "CSMSG_ROULETTE_CLICK", "Click" } ,
534 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
535 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
536 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
538 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
539 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
540 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
541 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
542 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
543 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
544 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
545 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
546 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
547 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
548 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
549 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
550 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
551 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
554 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
555 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
556 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
557 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
561 /* eject_user and unban_user flags */
562 #define ACTION_KICK 0x0001
563 #define ACTION_BAN 0x0002
564 #define ACTION_ADD_LAMER 0x0004
565 #define ACTION_ADD_TIMED_LAMER 0x0008
566 #define ACTION_UNBAN 0x0010
567 #define ACTION_DEL_LAMER 0x0020
569 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
570 #define MODELEN 40 + KEYLEN
574 #define CSFUNC_ARGS user, channel, argc, argv, cmd
576 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
577 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
578 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
579 reply("MSG_MISSING_PARAMS", argv[0]); \
583 DECLARE_LIST(dnrList
, struct do_not_register
*);
584 DEFINE_LIST(dnrList
, struct do_not_register
*);
586 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
588 struct userNode
*chanserv
;
591 extern struct string_list
*autojoin_channels
;
592 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
593 static struct log_type
*CS_LOG
;
594 struct adduserPending
* adduser_pendings
= NULL
;
595 unsigned int adduser_pendings_count
= 0;
596 unsigned long god_timeout
;
600 struct channelList support_channels
;
601 struct mod_chanmode default_modes
;
603 unsigned long db_backup_frequency
;
604 unsigned long channel_expire_frequency
;
605 unsigned long ban_timeout_frequency
;
608 unsigned int adjust_delay
;
609 long channel_expire_delay
;
610 unsigned int nodelete_level
;
612 unsigned int adjust_threshold
;
613 int join_flood_threshold
;
615 unsigned int greeting_length
;
616 unsigned int refresh_period
;
617 unsigned int giveownership_period
;
619 unsigned int max_owned
;
620 unsigned int max_chan_users
;
621 unsigned int max_chan_bans
; /* lamers */
622 unsigned int max_userinfo_length
;
623 unsigned int valid_channel_regex_set
: 1;
625 regex_t valid_channel_regex
;
627 struct string_list
*set_shows
;
628 struct string_list
*eightball
;
629 struct string_list
*old_ban_names
;
630 struct string_list
*wheel
;
632 const char *ctcp_short_ban_duration
;
633 const char *ctcp_long_ban_duration
;
635 const char *irc_operator_epithet
;
636 const char *network_helper_epithet
;
637 const char *support_helper_epithet
;
642 struct userNode
*user
;
643 struct userNode
*bot
;
644 struct chanNode
*channel
;
646 unsigned short lowest
;
647 unsigned short highest
;
648 struct userData
**users
;
649 struct helpfile_table table
;
652 enum note_access_type
654 NOTE_SET_CHANNEL_ACCESS
,
655 NOTE_SET_CHANNEL_SETTER
,
659 enum note_visible_type
662 NOTE_VIS_CHANNEL_USERS
,
666 struct io_fd
*socket_io_fd
;
667 extern struct cManagerNode cManager
;
671 enum note_access_type set_access_type
;
673 unsigned int min_opserv
;
674 unsigned short min_ulevel
;
676 enum note_visible_type visible_type
;
677 unsigned int max_length
;
684 struct note_type
*type
;
685 char setter
[NICKSERV_HANDLE_LEN
+1];
689 static unsigned int registered_channels
;
690 static unsigned int banCount
;
692 static const struct {
695 unsigned short level
;
697 } accessLevels
[] = { /* MUST be orderd less to most! */
698 { "pal", "Pal", UL_PEON
, '+' },
699 { "peon", "Peon", UL_PEON
, '+' },
700 { "halfop", "HalfOp", UL_HALFOP
, '%' },
701 { "op", "Op", UL_OP
, '@' },
702 { "manager", "Manager", UL_MANAGER
, '%' },
703 { "coowner", "Coowner", UL_COOWNER
, '*' },
704 { "owner", "Owner", UL_OWNER
, '!' },
705 { "helper", "BUG:", UL_HELPER
, 'X' }
708 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
709 static const struct {
712 unsigned short default_value
;
713 unsigned int old_idx
;
714 unsigned int old_flag
;
715 unsigned short flag_value
;
717 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
718 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
719 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
720 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
721 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
722 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
723 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
724 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
725 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
728 struct charOptionValues
{
731 } automodeValues
[] = {
732 { 'n', "CSMSG_AUTOMODE_NONE" },
733 { 'y', "CSMSG_AUTOMODE_NORMAL" },
734 { 'v', "CSMSG_AUTOMODE_VOICE" },
735 { 'h', "CSMSG_AUTOMODE_HOP" },
736 { 'o', "CSMSG_AUTOMODE_OP" },
737 { 'm', "CSMSG_AUTOMODE_MUTE" },
738 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
739 }, protectValues
[] = {
740 { 'a', "CSMSG_PROTECT_ALL" },
741 { 'e', "CSMSG_PROTECT_EQUAL" },
742 { 'l', "CSMSG_PROTECT_LOWER" },
743 { 'n', "CSMSG_PROTECT_NONE" }
745 { 'd', "CSMSG_TOYS_DISABLED" },
746 { 'n', "CSMSG_TOYS_PRIVATE" },
747 { 'p', "CSMSG_TOYS_PUBLIC" }
748 }, topicRefreshValues
[] = {
749 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
750 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
751 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
752 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
753 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
754 }, ctcpReactionValues
[] = {
755 { 'n', "CSMSG_CTCPREACTION_NONE" },
756 { 'k', "CSMSG_CTCPREACTION_KICK" },
757 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
758 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
759 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
760 }, banTimeoutValues
[] = {
761 { '0', "CSMSG_BANTIMEOUT_NONE" },
762 { '1', "CSMSG_BANTIMEOUT_10M" },
763 { '2', "CSMSG_BANTIMEOUT_2H" },
764 { '3', "CSMSG_BANTIMEOUT_4H" },
765 { '4', "CSMSG_BANTIMEOUT_1D" },
766 { '5', "CSMSG_BANTIMEOUT_1W" }
769 { 'n', "CSMSG_RESYNC_NEVER" },
770 { '1', "CSMSG_RESYNC_3_HOURS" },
771 { '2', "CSMSG_RESYNC_6_HOURS" },
772 { '3', "CSMSG_RESYNC_12_HOURS" },
773 { '4', "CSMSG_RESYNC_24_HOURS" }
776 static const struct {
780 unsigned int old_idx
;
782 struct charOptionValues
*values
;
784 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
785 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
786 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
787 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
788 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
789 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
790 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
793 struct userData
*helperList
;
794 struct chanData
*channelList
;
795 static struct module *chanserv_module
;
796 static unsigned int userCount
;
797 unsigned int chanserv_read_version
= 0; /* db version control */
799 #define CHANSERV_DB_VERSION 2
801 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
802 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
804 void sputsock(const char *text
, ...) PRINTF_LIKE(1, 2);
807 sputsock(const char *text
, ...)
813 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
815 va_start(arg_list
, text
);
816 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
818 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
820 log_replay(MAIN_LOG
, true, buffer
);
821 buffer
[pos
++] = '\n';
823 ioset_write(socket_io_fd
, buffer
, pos
);
827 user_level_from_name(const char *name
, unsigned short clamp_level
)
829 unsigned int level
= 0, ii
;
831 level
= strtoul(name
, NULL
, 10);
832 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
833 if(!irccasecmp(name
, accessLevels
[ii
].name
))
834 level
= accessLevels
[ii
].level
;
835 if(level
> clamp_level
)
841 user_level_name_from_level(int level
)
849 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
850 if(level
>= accessLevels
[ii
].level
)
851 highest
= accessLevels
[ii
].title
;
857 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
860 *minl
= strtoul(arg
, &sep
, 10);
868 *maxl
= strtoul(sep
+1, &sep
, 10);
876 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
878 struct userData
*uData
, **head
;
880 if(!channel
|| !handle
)
883 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
884 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
886 for(uData
= helperList
;
887 uData
&& uData
->handle
!= handle
;
888 uData
= uData
->next
);
892 uData
= calloc(1, sizeof(struct userData
));
893 uData
->handle
= handle
;
895 uData
->access
= UL_HELPER
;
901 uData
->next
= helperList
;
903 helperList
->prev
= uData
;
911 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
912 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
915 head
= &(channel
->users
);
918 if(uData
&& (uData
!= *head
))
920 /* Shuffle the user to the head of whatever list he was in. */
922 uData
->next
->prev
= uData
->prev
;
924 uData
->prev
->next
= uData
->next
;
930 (**head
).prev
= uData
;
937 /* Returns non-zero if user has at least the minimum access.
938 * exempt_owner is set when handling !set, so the owner can set things
941 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
943 struct userData
*uData
;
944 struct chanData
*cData
= channel
->channel_info
;
945 unsigned short minimum
= cData
->lvlOpts
[opt
];
948 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
951 if(minimum
<= uData
->access
)
953 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
958 /* Scan for other users authenticated to the same handle
959 still in the channel. If so, keep them listed as present.
961 user is optional, if not null, it skips checking that userNode
962 (for the handle_part function) */
964 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
968 if(IsSuspended(uData
->channel
)
969 || IsUserSuspended(uData
)
970 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
982 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
984 unsigned int eflags
, argc
;
986 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
988 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
989 if(!channel
->channel_info
990 || IsSuspended(channel
->channel_info
)
992 || !ircncasecmp(text
, "ACTION ", 7))
994 /* We dont punish people we know -Rubin
995 * * Figure out the minimum level needed to CTCP the channel *
997 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
1000 /* If they are a user of the channel, they are exempt */
1001 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
1003 /* We need to enforce against them; do so. */
1006 argv
[1] = user
->nick
;
1008 if(GetUserMode(channel
, user
))
1009 eflags
|= ACTION_KICK
;
1010 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
1011 default: case 'n': return;
1013 eflags
|= ACTION_KICK
;
1016 eflags
|= ACTION_BAN
;
1019 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1020 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
1023 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1024 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
1027 argv
[argc
++] = bad_ctcp_reason
;
1028 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
1032 chanserv_create_note_type(const char *name
)
1034 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
1035 strcpy(ntype
->name
, name
);
1037 dict_insert(note_types
, ntype
->name
, ntype
);
1042 chanserv_deref_note_type(void *data
)
1044 struct note_type
*ntype
= data
;
1046 if(--ntype
->refs
> 0)
1052 chanserv_flush_note_type(struct note_type
*ntype
)
1054 struct chanData
*cData
;
1055 for(cData
= channelList
; cData
; cData
= cData
->next
)
1056 dict_remove(cData
->notes
, ntype
->name
);
1060 chanserv_truncate_notes(struct note_type
*ntype
)
1062 struct chanData
*cData
;
1064 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1066 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1067 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1070 if(strlen(note
->note
) <= ntype
->max_length
)
1072 dict_remove2(cData
->notes
, ntype
->name
, 1);
1073 note
= realloc(note
, size
);
1074 note
->note
[ntype
->max_length
] = 0;
1075 dict_insert(cData
->notes
, ntype
->name
, note
);
1079 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1081 static struct note
*
1082 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1085 unsigned int len
= strlen(text
);
1087 if(len
> type
->max_length
) len
= type
->max_length
;
1088 note
= calloc(1, sizeof(*note
) + len
);
1090 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1091 memcpy(note
->note
, text
, len
);
1092 note
->note
[len
] = 0;
1093 dict_insert(channel
->notes
, type
->name
, note
);
1099 chanserv_free_note(void *data
)
1101 struct note
*note
= data
;
1103 chanserv_deref_note_type(note
->type
);
1104 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1108 static MODCMD_FUNC(cmd_createnote
) {
1109 struct note_type
*ntype
;
1110 unsigned int arg
= 1, existed
= 0, max_length
;
1112 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1115 ntype
= chanserv_create_note_type(argv
[arg
]);
1116 if(!irccasecmp(argv
[++arg
], "privileged"))
1119 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1120 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1122 else if(!irccasecmp(argv
[arg
], "channel"))
1124 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1127 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1130 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1131 ntype
->set_access
.min_ulevel
= ulvl
;
1133 else if(!irccasecmp(argv
[arg
], "setter"))
1135 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1139 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1143 if(!irccasecmp(argv
[++arg
], "privileged"))
1144 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1145 else if(!irccasecmp(argv
[arg
], "channel_users"))
1146 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1147 else if(!irccasecmp(argv
[arg
], "all"))
1148 ntype
->visible_type
= NOTE_VIS_ALL
;
1150 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1154 if((arg
+1) >= argc
) {
1155 reply("MSG_MISSING_PARAMS", argv
[0]);
1158 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1159 if(max_length
< 20 || max_length
> 450)
1161 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1164 if(existed
&& (max_length
< ntype
->max_length
))
1166 ntype
->max_length
= max_length
;
1167 chanserv_truncate_notes(ntype
);
1169 ntype
->max_length
= max_length
;
1172 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1174 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1179 dict_remove(note_types
, ntype
->name
);
1183 static MODCMD_FUNC(cmd_removenote
) {
1184 struct note_type
*ntype
;
1187 ntype
= dict_find(note_types
, argv
[1], NULL
);
1188 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1191 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1198 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1201 chanserv_flush_note_type(ntype
);
1203 dict_remove(note_types
, argv
[1]);
1204 reply("CSMSG_NOTE_DELETED", argv
[1]);
1209 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1213 if(orig
->modes_set
& change
->modes_clear
)
1215 if(orig
->modes_clear
& change
->modes_set
)
1217 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1218 && strcmp(orig
->new_key
, change
->new_key
))
1220 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1221 && (orig
->new_limit
!= change
->new_limit
))
1226 static char max_length_text
[MAXLEN
+1][16];
1228 static struct helpfile_expansion
1229 chanserv_expand_variable(const char *variable
)
1231 struct helpfile_expansion exp
;
1233 if(!irccasecmp(variable
, "notes"))
1236 exp
.type
= HF_TABLE
;
1237 exp
.value
.table
.length
= 1;
1238 exp
.value
.table
.width
= 3;
1239 exp
.value
.table
.flags
= 0;
1240 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1241 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1242 exp
.value
.table
.contents
[0][0] = "Note Type";
1243 exp
.value
.table
.contents
[0][1] = "Visibility";
1244 exp
.value
.table
.contents
[0][2] = "Max Length";
1245 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1247 struct note_type
*ntype
= iter_data(it
);
1250 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1251 row
= exp
.value
.table
.length
++;
1252 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1253 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1254 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1255 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1257 if(!max_length_text
[ntype
->max_length
][0])
1258 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1259 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1264 exp
.type
= HF_STRING
;
1265 exp
.value
.str
= NULL
;
1269 static struct chanData
*
1270 register_channel(struct chanNode
*cNode
, char *registrar
)
1272 struct chanData
*channel
;
1273 enum levelOption lvlOpt
;
1274 enum charOption chOpt
;
1276 channel
= calloc(1, sizeof(struct chanData
));
1278 channel
->notes
= dict_new();
1279 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1281 channel
->registrar
= strdup(registrar
);
1282 channel
->registered
= now
;
1283 channel
->visited
= now
;
1284 channel
->limitAdjusted
= now
;
1285 channel
->ownerTransfer
= now
;
1286 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1287 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1288 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1289 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1290 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1292 channel
->prev
= NULL
;
1293 channel
->next
= channelList
;
1296 channelList
->prev
= channel
;
1297 channelList
= channel
;
1298 registered_channels
++;
1300 channel
->channel
= cNode
;
1302 cNode
->channel_info
= channel
;
1307 static struct userData
*
1308 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
, time_t accessexpiry
)
1310 struct userData
*ud
;
1312 if(access
> UL_OWNER
)
1315 ud
= calloc(1, sizeof(*ud
));
1316 ud
->channel
= channel
;
1317 ud
->handle
= handle
;
1319 ud
->access
= access
;
1320 ud
->info
= info
? strdup(info
) : NULL
;
1321 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1326 ud
->next
= channel
->users
;
1328 channel
->users
->prev
= ud
;
1329 channel
->users
= ud
;
1331 channel
->userCount
++;
1335 ud
->u_next
= ud
->handle
->channels
;
1337 ud
->u_next
->u_prev
= ud
;
1338 ud
->handle
->channels
= ud
;
1340 ud
->flags
= USER_FLAGS_DEFAULT
;
1344 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1347 chanserv_expire_tempuser(void *data
)
1349 struct userData
*uData
= data
;
1353 handle
= strdup(uData
->handle
->handle
);
1354 if (uData
->accessexpiry
> 0) {
1355 if (uData
->present
) {
1356 struct userNode
*user
, *next_un
= NULL
;
1357 struct handle_info
*hi
;
1359 hi
= get_handle_info(handle
);
1360 for (user
= hi
->users
; user
; user
= next_un
) {
1361 struct mod_chanmode
*change
;
1362 struct modeNode
*mn
;
1363 unsigned int count
= 0;
1365 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1366 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1367 next_un
= user
->next_authed
;
1371 change
= mod_chanmode_alloc(2);
1372 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1373 change
->args
[count
++].u
.member
= mn
;
1376 change
->argc
= count
;
1377 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1379 mod_chanmode_free(change
);
1380 next_un
= user
->next_authed
;
1383 del_channel_user(uData
, 1);
1389 chanserv_expire_tempclvl(void *data
)
1391 struct userData
*uData
= data
;
1395 handle
= strdup(uData
->handle
->handle
);
1396 if (uData
->clvlexpiry
> 0) {
1397 int changemodes
= 0;
1398 unsigned int mode
= 0;
1400 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1402 mode
= MODE_REMOVE
| MODE_CHANOP
;
1403 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1405 mode
= MODE_REMOVE
| MODE_HALFOP
;
1409 if (uData
->present
) {
1410 struct userNode
*user
, *next_un
= NULL
;
1411 struct handle_info
*hi
;
1413 hi
= get_handle_info(handle
);
1414 for (user
= hi
->users
; user
; user
= next_un
) {
1415 struct mod_chanmode
*change
;
1416 struct modeNode
*mn
;
1417 unsigned int count
= 0;
1419 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1420 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1421 next_un
= user
->next_authed
;
1425 if (changemodes
== 0) {
1426 next_un
= user
->next_authed
;
1430 change
= mod_chanmode_alloc(2);
1431 change
->args
[count
].mode
= mode
;
1432 change
->args
[count
++].u
.member
= mn
;
1435 change
->argc
= count
;
1436 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1438 mod_chanmode_free(change
);
1439 next_un
= user
->next_authed
;
1443 uData
->access
= uData
->lastaccess
;
1444 uData
->lastaccess
= 0;
1445 uData
->clvlexpiry
= 0;
1451 del_channel_user(struct userData
*user
, int do_gc
)
1453 struct chanData
*channel
= user
->channel
;
1455 channel
->userCount
--;
1458 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1459 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1462 user
->prev
->next
= user
->next
;
1464 channel
->users
= user
->next
;
1466 user
->next
->prev
= user
->prev
;
1469 user
->u_prev
->u_next
= user
->u_next
;
1471 user
->handle
->channels
= user
->u_next
;
1473 user
->u_next
->u_prev
= user
->u_prev
;
1477 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1478 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1479 unregister_channel(channel
, "lost all users.");
1483 static struct adduserPending
*
1484 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1486 struct adduserPending
*ap
;
1487 ap
= calloc(1,sizeof(struct adduserPending
));
1488 ap
->channel
= channel
;
1491 ap
->created
= time(NULL
);
1493 /* ap->prev defaults to NULL already.. */
1494 ap
->next
= adduser_pendings
;
1495 if(adduser_pendings
)
1496 adduser_pendings
->prev
= ap
;
1497 adduser_pendings
= ap
;
1498 adduser_pendings_count
++;
1503 del_adduser_pending(struct adduserPending
*ap
)
1506 ap
->prev
->next
= ap
->next
;
1508 adduser_pendings
= ap
->next
;
1511 ap
->next
->prev
= ap
->prev
;
1515 static void expire_adduser_pending();
1517 /* find_adduser_pending(channel, user) will find an arbitrary record
1518 * from user, channel, or user and channel.
1519 * if user or channel are NULL, they will match any records.
1521 static struct adduserPending
*
1522 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1524 struct adduserPending
*ap
;
1526 expire_adduser_pending(); /* why not here.. */
1528 if(!channel
&& !user
) /* 2 nulls matches all */
1529 return(adduser_pendings
);
1530 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1532 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1539 /* Remove all pendings for a user or channel
1541 * called in nickserv.c DelUser() and proto-* unregister_channel()
1544 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1546 struct adduserPending
*ap
;
1548 /* So this is a bit wastefull, i hate dealing with linked lists.
1549 * if its a problem we'll rewrite it right */
1550 while((ap
= find_adduser_pending(channel
, user
))) {
1551 del_adduser_pending(ap
);
1555 /* Called from nickserv.c cmd_auth after someone auths */
1557 process_adduser_pending(struct userNode
*user
)
1559 struct adduserPending
*ap
;
1560 if(!user
->handle_info
)
1561 return; /* not associated with an account */
1562 while((ap
= find_adduser_pending(NULL
, user
)))
1564 struct userData
*actee
;
1565 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1567 /* Already on the userlist. do nothing*/
1571 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1572 scan_user_presence(actee
, NULL
);
1574 del_adduser_pending(ap
);
1579 expire_adduser_pending()
1581 struct adduserPending
*ap
, *ap_next
;
1582 ap
= adduser_pendings
;
1585 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1587 ap_next
= ap
->next
; /* save next */
1588 del_adduser_pending(ap
); /* free and relink */
1589 ap
= ap_next
; /* advance */
1596 static void expire_ban(void *data
);
1599 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1602 unsigned int ii
, l1
, l2
;
1607 bd
= malloc(sizeof(struct banData
));
1609 bd
->channel
= channel
;
1611 bd
->triggered
= triggered
;
1612 bd
->expires
= expires
;
1614 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1616 extern const char *hidden_host_suffix
;
1617 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1621 l2
= strlen(old_name
);
1624 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1626 new_mask
= alloca(MAXLEN
);
1627 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1630 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1632 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1633 bd
->reason
= strdup(reason
);
1636 timeq_add(expires
, expire_ban
, bd
);
1639 bd
->next
= channel
->bans
; /* lamers */
1641 channel
->bans
->prev
= bd
;
1643 channel
->banCount
++;
1650 del_channel_ban(struct banData
*ban
)
1652 ban
->channel
->banCount
--;
1656 ban
->prev
->next
= ban
->next
;
1658 ban
->channel
->bans
= ban
->next
;
1661 ban
->next
->prev
= ban
->prev
;
1664 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1673 expire_ban(void *data
) /* lamer.. */
1675 struct banData
*bd
= data
;
1676 if(!IsSuspended(bd
->channel
))
1678 struct banList bans
;
1679 struct mod_chanmode change
;
1681 bans
= bd
->channel
->channel
->banlist
;
1682 mod_chanmode_init(&change
);
1683 for(ii
=0; ii
<bans
.used
; ii
++)
1685 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1688 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1689 change
.args
[0].u
.hostmask
= bd
->mask
;
1690 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1696 del_channel_ban(bd
);
1699 static void chanserv_expire_suspension(void *data
);
1702 unregister_channel(struct chanData
*channel
, const char *reason
)
1704 struct mod_chanmode change
;
1705 char msgbuf
[MAXLEN
];
1707 /* After channel unregistration, the following must be cleaned
1709 - Channel information.
1711 - Channel bans. (lamers)
1712 - Channel suspension data.
1713 - adduser_pending data.
1714 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1720 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1724 mod_chanmode_init(&change
);
1725 change
.modes_clear
|= MODE_REGISTERED
;
1726 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1729 wipe_adduser_pending(channel
->channel
, NULL
);
1731 while(channel
->users
)
1732 del_channel_user(channel
->users
, 0);
1734 while(channel
->bans
)
1735 del_channel_ban(channel
->bans
);
1737 free(channel
->topic
);
1738 free(channel
->registrar
);
1739 free(channel
->greeting
);
1740 free(channel
->user_greeting
);
1741 free(channel
->topic_mask
);
1744 channel
->prev
->next
= channel
->next
;
1746 channelList
= channel
->next
;
1749 channel
->next
->prev
= channel
->prev
;
1751 if(channel
->suspended
)
1753 struct chanNode
*cNode
= channel
->channel
;
1754 struct suspended
*suspended
, *next_suspended
;
1756 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1758 next_suspended
= suspended
->previous
;
1759 free(suspended
->suspender
);
1760 free(suspended
->reason
);
1761 if(suspended
->expires
)
1762 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1767 cNode
->channel_info
= NULL
;
1769 channel
->channel
->channel_info
= NULL
;
1771 dict_delete(channel
->notes
);
1772 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1773 if(!IsSuspended(channel
))
1774 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1775 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1776 UnlockChannel(channel
->channel
);
1778 registered_channels
--;
1782 expire_channels(UNUSED_ARG(void *data
))
1784 struct chanData
*channel
, *next
;
1785 struct userData
*user
;
1786 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1788 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1789 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1791 for(channel
= channelList
; channel
; channel
= next
)
1793 next
= channel
->next
;
1795 /* See if the channel can be expired. */
1796 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1797 || IsProtected(channel
))
1800 /* Make sure there are no high-ranking users still in the channel. */
1801 for(user
=channel
->users
; user
; user
=user
->next
)
1802 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1807 /* Unregister the channel */
1808 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1809 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1810 unregister_channel(channel
, "registration expired.");
1813 if(chanserv_conf
.channel_expire_frequency
)
1814 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1818 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1820 char protect
= channel
->chOpts
[chProtect
];
1821 struct userData
*cs_victim
, *cs_aggressor
;
1823 /* If victim access level is greater than set invitelevel, don't let
1824 * us kick them, but don't consider it punishment if someone else does
1828 if(victim
== aggressor
)
1830 /* Don't protect if the victim isn't authenticated (because they
1831 can't be a channel user), unless we are to protect non-users
1834 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1836 /* If they have enough access to invite themselvs through a ban,
1837 * and its us kicking them, don't. -Rubin */
1838 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1844 if(protect
!= 'a' && !cs_victim
)
1847 /* Protect if the aggressor isn't a user because at this point,
1848 the aggressor can only be less than or equal to the victim. */
1850 /* Not protected from chanserv except above */
1851 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1852 if(aggressor
== chanserv
)
1855 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1859 /* If the aggressor was a user, then the victim can't be helped. */
1866 if(cs_victim
->access
> cs_aggressor
->access
)
1871 if(cs_victim
->access
>= cs_aggressor
->access
)
1880 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1882 struct chanData
*cData
= channel
->channel_info
;
1883 struct userData
*cs_victim
;
1885 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1886 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1887 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1890 reply("CSMSG_OPBY_LOCKED");
1892 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1900 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1902 struct chanData
*cData
= channel
->channel_info
;
1903 struct userData
*cs_victim
;
1905 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1906 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1907 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1909 reply("CSMSG_HOPBY_LOCKED");
1918 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1920 if(IsService(victim
))
1922 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1926 if(protect_user(victim
, user
, channel
->channel_info
, false))
1928 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1936 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1938 if(IsService(victim
))
1940 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1944 if(protect_user(victim
, user
, channel
->channel_info
, false))
1946 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1953 static struct do_not_register
*
1954 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1956 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1957 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1958 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1959 strcpy(dnr
->reason
, reason
);
1961 if(dnr
->chan_name
[0] == '*')
1962 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1963 else if(strpbrk(dnr
->chan_name
, "*?"))
1964 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1966 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1970 static struct dnrList
1971 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1973 struct dnrList list
;
1975 struct do_not_register
*dnr
;
1977 dnrList_init(&list
);
1978 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1979 dnrList_append(&list
, dnr
);
1980 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1981 dnrList_append(&list
, dnr
);
1983 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1984 if(match_ircglob(chan_name
, iter_key(it
)))
1985 dnrList_append(&list
, iter_data(it
));
1990 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1992 struct dnrList list
;
1993 struct do_not_register
*dnr
;
1995 char buf
[INTERVALLEN
];
1997 list
= chanserv_find_dnrs(chan_name
, handle
);
1998 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
2000 dnr
= list
.list
[ii
];
2003 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
2004 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
2007 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2010 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2015 struct do_not_register
*
2016 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2018 struct do_not_register
*dnr
;
2021 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
2025 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
2027 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
2028 if(match_ircglob(chan_name
, iter_key(it
)))
2029 return iter_data(it
);
2034 static CHANSERV_FUNC(cmd_noregister
)
2037 struct do_not_register
*dnr
;
2038 char buf
[INTERVALLEN
];
2039 unsigned int matches
;
2045 reply("CSMSG_DNR_SEARCH_RESULTS");
2046 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2049 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
2051 dnr
= iter_data(it
);
2053 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2055 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2058 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
2060 dnr
= iter_data(it
);
2062 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2064 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2067 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
2069 dnr
= iter_data(it
);
2071 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2073 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2078 reply("MSG_MATCH_COUNT", matches
);
2080 reply("MSG_NO_MATCHES");
2086 if(!IsChannelName(target
) && (*target
!= '*'))
2088 reply("CSMSG_NOT_DNR", target
);
2094 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
2095 if((*target
== '*') && !get_handle_info(target
+ 1))
2097 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2100 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
2101 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2105 reply("CSMSG_DNR_SEARCH_RESULTS");
2106 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2109 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2111 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2113 reply("MSG_NO_MATCHES");
2117 static CHANSERV_FUNC(cmd_allowregister
)
2119 const char *chan_name
= argv
[1];
2121 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
2123 dict_remove(handle_dnrs
, chan_name
+1);
2124 reply("CSMSG_DNR_REMOVED", chan_name
);
2126 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
2128 dict_remove(plain_dnrs
, chan_name
);
2129 reply("CSMSG_DNR_REMOVED", chan_name
);
2131 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
2133 dict_remove(mask_dnrs
, chan_name
);
2134 reply("CSMSG_DNR_REMOVED", chan_name
);
2138 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2145 chanserv_get_owned_count(struct handle_info
*hi
)
2147 struct userData
*cList
;
2150 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2151 if(cList
->access
== UL_OWNER
)
2156 static CHANSERV_FUNC(cmd_register
)
2158 struct handle_info
*handle
;
2159 struct chanData
*cData
;
2160 struct modeNode
*mn
;
2161 char reason
[MAXLEN
];
2163 unsigned int new_channel
, force
=0;
2164 struct do_not_register
*dnr
;
2167 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2168 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2174 if(channel
->channel_info
)
2176 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2180 if(channel
->bad_channel
)
2182 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2186 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2188 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2193 chan_name
= channel
->name
;
2199 reply("MSG_MISSING_PARAMS", cmd
->name
);
2200 svccmd_send_help_brief(user
, chanserv
, cmd
);
2203 if(!IsChannelName(argv
[1]))
2205 reply("MSG_NOT_CHANNEL_NAME");
2209 if(opserv_bad_channel(argv
[1]))
2211 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2216 chan_name
= argv
[1];
2219 if(argc
>= (new_channel
+2))
2221 if(!IsHelping(user
))
2223 reply("CSMSG_PROXY_FORBIDDEN");
2227 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2229 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2230 dnr
= chanserv_is_dnr(chan_name
, handle
);
2232 /* Check if they are over the limit.. */
2233 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2235 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2242 handle
= user
->handle_info
;
2243 dnr
= chanserv_is_dnr(chan_name
, handle
);
2244 /* Check if they are over the limit.. */
2245 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2247 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2250 /* Check if another service is in the channel */
2252 for(n
= 0; n
< channel
->members
.used
; n
++)
2254 mn
= channel
->members
.list
[n
];
2255 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2257 reply("CSMSG_ANOTHER_SERVICE");
2264 if(!IsHelping(user
))
2265 reply("CSMSG_DNR_CHANNEL", chan_name
);
2267 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2271 /* now handled above for message specilization *
2272 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2274 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2279 if (chanserv_conf
.valid_channel_regex_set
) {
2280 int err
= regexec(&chanserv_conf
.valid_channel_regex
, argv
[1], 0, 0, 0);
2283 buff
[regerror(err
, &chanserv_conf
.valid_channel_regex
, buff
, sizeof(buff
))] = 0;
2284 log_module(CS_LOG
, LOG_INFO
, "regexec error: %s (%d)", buff
, err
);
2286 if(err
== REG_NOMATCH
) {
2287 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2293 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2295 cData
= register_channel(channel
, user
->handle_info
->handle
);
2296 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2297 cData
->modes
= chanserv_conf
.default_modes
;
2299 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2300 if (IsOffChannel(cData
))
2302 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2306 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2307 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2308 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2310 mod_chanmode_announce(chanserv
, channel
, change
);
2311 mod_chanmode_free(change
);
2314 /* Initialize the channel's max user record. */
2315 cData
->max
= channel
->members
.used
;
2316 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2318 if(handle
!= user
->handle_info
)
2319 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2322 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2323 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2324 handle
->handle
, user
->handle_info
->handle
);
2329 make_confirmation_string(struct userData
*uData
)
2331 static char strbuf
[16];
2336 for(src
= uData
->handle
->handle
; *src
; )
2337 accum
= accum
* 31 + toupper(*src
++);
2339 for(src
= uData
->channel
->channel
->name
; *src
; )
2340 accum
= accum
* 31 + toupper(*src
++);
2341 sprintf(strbuf
, "%08x", accum
);
2345 static CHANSERV_FUNC(cmd_unregister
)
2348 char reason
[MAXLEN
];
2349 struct chanData
*cData
;
2350 struct userData
*uData
;
2352 cData
= channel
->channel_info
;
2355 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2359 uData
= GetChannelUser(cData
, user
->handle_info
);
2360 if(!uData
|| (uData
->access
< UL_OWNER
))
2362 reply("CSMSG_NO_ACCESS");
2366 if(IsProtected(cData
))
2368 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2372 if(!IsHelping(user
))
2374 const char *confirm_string
;
2375 if(IsSuspended(cData
))
2377 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2380 confirm_string
= make_confirmation_string(uData
);
2381 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2383 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2388 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2389 name
= strdup(channel
->name
);
2390 unregister_channel(cData
, reason
);
2391 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2392 reply("CSMSG_UNREG_SUCCESS", name
);
2398 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2400 extern struct userNode
*spamserv
;
2401 struct mod_chanmode
*change
;
2403 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2405 change
= mod_chanmode_alloc(2);
2407 change
->args
[0].mode
= MODE_CHANOP
;
2408 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2409 change
->args
[1].mode
= MODE_CHANOP
;
2410 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2414 change
= mod_chanmode_alloc(1);
2416 change
->args
[0].mode
= MODE_CHANOP
;
2417 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2420 mod_chanmode_announce(chanserv
, channel
, change
);
2421 mod_chanmode_free(change
);
2424 static CHANSERV_FUNC(cmd_move
)
2426 struct mod_chanmode change
;
2427 struct chanNode
*target
;
2428 struct modeNode
*mn
;
2429 struct userData
*uData
;
2430 struct do_not_register
*dnr
;
2431 int chanserv_join
= 0, spamserv_join
;
2435 if(IsProtected(channel
->channel_info
))
2437 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2441 if(!IsChannelName(argv
[1]))
2443 reply("MSG_NOT_CHANNEL_NAME");
2447 if(opserv_bad_channel(argv
[1]))
2449 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2453 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2455 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2457 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2459 if(!IsHelping(user
))
2460 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2462 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2468 mod_chanmode_init(&change
);
2469 if(!(target
= GetChannel(argv
[1])))
2471 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2472 if(!IsSuspended(channel
->channel_info
))
2475 else if(target
->channel_info
)
2477 reply("CSMSG_ALREADY_REGGED", target
->name
);
2480 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2481 && !IsHelping(user
))
2483 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2486 else if(!IsSuspended(channel
->channel_info
))
2491 /* Clear MODE_REGISTERED from old channel, add it to new. */
2493 change
.modes_clear
= MODE_REGISTERED
;
2494 mod_chanmode_announce(chanserv
, channel
, &change
);
2495 change
.modes_clear
= 0;
2496 change
.modes_set
= MODE_REGISTERED
;
2497 mod_chanmode_announce(chanserv
, target
, &change
);
2500 /* Move the channel_info to the target channel; it
2501 shouldn't be necessary to clear timeq callbacks
2502 for the old channel. */
2503 target
->channel_info
= channel
->channel_info
;
2504 target
->channel_info
->channel
= target
;
2505 channel
->channel_info
= NULL
;
2507 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2510 ss_cs_join_channel(target
, spamserv_join
);
2512 if(!IsSuspended(target
->channel_info
))
2514 char reason2
[MAXLEN
];
2515 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2516 DelChannelUser(chanserv
, channel
, reason2
, 0);
2519 UnlockChannel(channel
);
2520 LockChannel(target
);
2521 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2522 channel
->name
, target
->name
, user
->handle_info
->handle
);
2524 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2529 merge_users(struct chanData
*source
, struct chanData
*target
)
2531 struct userData
*suData
, *tuData
, *next
;
2537 /* Insert the source's users into the scratch area. */
2538 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2539 dict_insert(merge
, suData
->handle
->handle
, suData
);
2541 /* Iterate through the target's users, looking for
2542 users common to both channels. The lower access is
2543 removed from either the scratch area or target user
2545 for(tuData
= target
->users
; tuData
; tuData
= next
)
2547 struct userData
*choice
;
2549 next
= tuData
->next
;
2551 /* If a source user exists with the same handle as a target
2552 channel's user, resolve the conflict by removing one. */
2553 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2557 /* Pick the data we want to keep. */
2558 /* If the access is the same, use the later seen time. */
2559 if(suData
->access
== tuData
->access
)
2560 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2561 else /* Otherwise, keep the higher access level. */
2562 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2564 /* Remove the user that wasn't picked. */
2565 if(choice
== tuData
)
2567 dict_remove(merge
, suData
->handle
->handle
);
2568 del_channel_user(suData
, 0);
2571 del_channel_user(tuData
, 0);
2574 /* Move the remaining users to the target channel. */
2575 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2577 suData
= iter_data(it
);
2579 /* Insert the user into the target channel's linked list. */
2580 suData
->prev
= NULL
;
2581 suData
->next
= target
->users
;
2582 suData
->channel
= target
;
2585 target
->users
->prev
= suData
;
2586 target
->users
= suData
;
2588 /* Update the user counts for the target channel; the
2589 source counts are left alone. */
2590 target
->userCount
++;
2593 /* Possible to assert (source->users == NULL) here. */
2594 source
->users
= NULL
;
2599 merge_bans(struct chanData
*source
, struct chanData
*target
)
2601 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2603 /* Hold on to the original head of the target ban list
2604 to avoid comparing source bans with source bans. */
2605 tFront
= target
->bans
;
2607 /* Perform a totally expensive O(n*m) merge, ick. */
2608 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2610 /* Flag to track whether the ban's been moved
2611 to the destination yet. */
2614 /* Possible to assert (sbData->prev == NULL) here. */
2615 sNext
= sbData
->next
;
2617 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2619 tNext
= tbData
->next
;
2621 /* Perform two comparisons between each source
2622 and target ban, conflicts are resolved by
2623 keeping the broader ban and copying the later
2624 expiration and triggered time. */
2625 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2627 /* There is a broader ban in the target channel that
2628 overrides one in the source channel; remove the
2629 source ban and break. */
2630 if(sbData
->expires
> tbData
->expires
)
2631 tbData
->expires
= sbData
->expires
;
2632 if(sbData
->triggered
> tbData
->triggered
)
2633 tbData
->triggered
= sbData
->triggered
;
2634 del_channel_ban(sbData
);
2637 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2639 /* There is a broader ban in the source channel that
2640 overrides one in the target channel; remove the
2641 target ban, fall through and move the source over. */
2642 if(tbData
->expires
> sbData
->expires
)
2643 sbData
->expires
= tbData
->expires
;
2644 if(tbData
->triggered
> sbData
->triggered
)
2645 sbData
->triggered
= tbData
->triggered
;
2646 if(tbData
== tFront
)
2648 del_channel_ban(tbData
);
2651 /* Source bans can override multiple target bans, so
2652 we allow a source to run through this loop multiple
2653 times, but we can only move it once. */
2658 /* Remove the source ban from the source ban list. */
2660 sbData
->next
->prev
= sbData
->prev
;
2662 /* Modify the source ban's associated channel. */
2663 sbData
->channel
= target
;
2665 /* Insert the ban into the target channel's linked list. */
2666 sbData
->prev
= NULL
;
2667 sbData
->next
= target
->bans
;
2670 target
->bans
->prev
= sbData
;
2671 target
->bans
= sbData
;
2673 /* Update the user counts for the target channel. */
2678 /* Possible to assert (source->bans == NULL) here. */
2679 source
->bans
= NULL
;
2683 merge_data(struct chanData
*source
, struct chanData
*target
)
2685 /* Use more recent visited and owner-transfer time; use older
2686 * registered time. Bitwise or may_opchan. Use higher max.
2687 * Do not touch last_refresh, ban count or user counts.
2689 if(source
->visited
> target
->visited
)
2690 target
->visited
= source
->visited
;
2691 if(source
->registered
< target
->registered
)
2692 target
->registered
= source
->registered
;
2693 if(source
->ownerTransfer
> target
->ownerTransfer
)
2694 target
->ownerTransfer
= source
->ownerTransfer
;
2695 if(source
->may_opchan
)
2696 target
->may_opchan
= 1;
2697 if(source
->max
> target
->max
)
2698 target
->max
= source
->max
;
2702 merge_channel(struct chanData
*source
, struct chanData
*target
)
2704 merge_users(source
, target
);
2705 merge_bans(source
, target
);
2706 merge_data(source
, target
);
2709 static CHANSERV_FUNC(cmd_merge
)
2711 struct userData
*target_user
;
2712 struct chanNode
*target
;
2713 char reason
[MAXLEN
];
2718 /* Make sure the target channel exists and is registered to the user
2719 performing the command. */
2720 if(!(target
= GetChannel(argv
[1])))
2722 reply("MSG_INVALID_CHANNEL");
2727 if (!irccasecmp("nodelete", argv
[2]))
2731 if(!target
->channel_info
)
2733 reply("CSMSG_NOT_REGISTERED", target
->name
);
2737 if(IsProtected(channel
->channel_info
))
2739 reply("CSMSG_MERGE_NODELETE");
2743 if(IsSuspended(target
->channel_info
))
2745 reply("CSMSG_MERGE_SUSPENDED");
2749 if(channel
== target
)
2751 reply("CSMSG_MERGE_SELF");
2755 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2756 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2758 reply("CSMSG_MERGE_NOT_OWNER");
2762 /* Merge the channel structures and associated data. */
2763 merge_channel(channel
->channel_info
, target
->channel_info
);
2764 spamserv_cs_move_merge(user
, channel
, target
, 0);
2765 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2767 unregister_channel(channel
->channel_info
, reason
);
2768 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2772 static CHANSERV_FUNC(cmd_opchan
)
2774 struct mod_chanmode change
;
2775 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2777 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2780 if(!IsInChannel(channel
,chanserv
)) {
2781 reply("CSMSG_NOT_IN_CHANNEL", channel
->name
);
2784 channel
->channel_info
->may_opchan
= 0;
2785 mod_chanmode_init(&change
);
2787 change
.args
[0].mode
= MODE_CHANOP
;
2788 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2789 mod_chanmode_announce(chanserv
, channel
, &change
);
2790 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2794 static CHANSERV_FUNC(cmd_adduser
)
2796 struct userData
*actee
;
2797 struct userData
*actor
;
2798 struct handle_info
*handle
;
2799 unsigned short access
;
2803 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2805 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2809 access
= user_level_from_name(argv
[2], UL_OWNER
);
2812 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2816 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2817 if(actor
->access
<= access
)
2819 reply("CSMSG_NO_BUMP_ACCESS");
2823 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2825 // 'kevin must first authenticate with AuthServ.' is sent to user
2826 struct userNode
*unode
;
2827 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2830 if(find_adduser_pending(channel
, unode
)) {
2831 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2834 if(IsInChannel(channel
, unode
)) {
2835 reply("CSMSG_ADDUSER_PENDING");
2836 add_adduser_pending(channel
, unode
, access
);
2837 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2839 /* this results in user must auth AND not in chan errors. too confusing..
2841 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2849 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2851 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2855 time_t accessexpiry
= 0;
2856 unsigned int duration
= 0;
2858 if ((duration
= ParseInterval(argv
[3])))
2859 accessexpiry
= now
+ duration
;
2862 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
, accessexpiry
);
2863 scan_user_presence(actee
, NULL
);
2866 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
2868 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2872 static CHANSERV_FUNC(cmd_clvl
)
2874 struct handle_info
*handle
;
2875 struct userData
*victim
;
2876 struct userData
*actor
;
2877 unsigned short new_access
;
2878 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2882 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2884 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2887 if(handle
== user
->handle_info
&& !privileged
)
2889 reply("CSMSG_NO_SELF_CLVL");
2893 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2895 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2899 if(actor
->access
<= victim
->access
&& !privileged
)
2901 reply("MSG_USER_OUTRANKED", handle
->handle
);
2905 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2909 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2913 if(new_access
>= actor
->access
&& !privileged
)
2915 reply("CSMSG_NO_BUMP_ACCESS");
2919 time_t clvlexpiry
= 0;
2920 unsigned int duration
= 0;
2922 if ((duration
= ParseInterval(argv
[3])))
2923 clvlexpiry
= now
+ duration
;
2927 if (victim
->accessexpiry
> 0) {
2928 reply("CSMSG_NO_BUMP_EXPIRY");
2932 victim
->clvlexpiry
= clvlexpiry
;
2933 victim
->lastaccess
= victim
->access
;
2934 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
2937 victim
->access
= new_access
;
2938 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2942 static CHANSERV_FUNC(cmd_deluser
)
2944 struct handle_info
*handle
;
2945 struct userData
*victim
;
2946 struct userData
*actor
;
2947 unsigned short access
;
2952 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2954 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2957 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2959 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2965 access
= user_level_from_name(argv
[1], UL_OWNER
);
2966 char *useraccess
= user_level_name_from_level(victim
->access
);
2969 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2972 if(strcasecmp(argv
[1], useraccess
))
2974 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2980 access
= victim
->access
;
2983 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2985 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2989 chan_name
= strdup(channel
->name
);
2990 del_channel_user(victim
, 1);
2991 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2997 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2999 struct userData
*actor
, *uData
, *next
;
3001 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3003 if(min_access
> max_access
)
3005 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3009 if((actor
->access
<= max_access
) && !IsHelping(user
))
3011 reply("CSMSG_NO_ACCESS");
3015 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3019 if((uData
->access
>= min_access
)
3020 && (uData
->access
<= max_access
)
3021 && match_ircglob(uData
->handle
->handle
, mask
))
3022 del_channel_user(uData
, 1);
3025 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3029 static CHANSERV_FUNC(cmd_mdelowner
)
3031 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3034 static CHANSERV_FUNC(cmd_mdelcoowner
)
3036 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3039 static CHANSERV_FUNC(cmd_mdelmanager
)
3041 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3044 static CHANSERV_FUNC(cmd_mdelop
)
3046 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3049 static CHANSERV_FUNC(cmd_mdelhalfop
)
3051 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3054 static CHANSERV_FUNC(cmd_mdelpeon
)
3056 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3059 static CHANSERV_FUNC(cmd_mdelpal
)
3061 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3064 static CHANSERV_FUNC(cmd_levels
)
3066 struct helpfile_table tbl
;
3069 tbl
.length
= 6 + 1; // 6 levels
3072 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3073 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3074 tbl
.contents
[0][0] = "Level";
3075 tbl
.contents
[0][1] = "From";
3076 tbl
.contents
[0][2] = "-";
3077 tbl
.contents
[0][3] = "To";
3079 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3080 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3081 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3082 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3083 tbl
.contents
[ii
][3] = msnprintf(1, "");
3085 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3086 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3087 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3088 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3089 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3091 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3092 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3093 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3094 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3095 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3097 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3098 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3099 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3100 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3101 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3103 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3104 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3105 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3106 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3107 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3109 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3110 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3111 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3112 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3113 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3115 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3119 reply("CSMSG_LEVELS_HEADER");
3120 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3121 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3122 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3123 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3124 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3125 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3132 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3134 struct banData
*bData
, *next
;
3135 char interval
[INTERVALLEN
];
3140 limit
= now
- duration
;
3141 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3145 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3148 del_channel_ban(bData
);
3152 intervalString(interval
, duration
, user
->handle_info
);
3153 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3158 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
)
3160 struct userData
*actor
, *uData
, *next
;
3161 char interval
[INTERVALLEN
];
3165 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3166 if(min_access
> max_access
)
3168 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3172 if((actor
->access
<= max_access
) && !IsHelping(user
))
3174 reply("CSMSG_NO_ACCESS");
3179 limit
= now
- duration
;
3180 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3184 if((uData
->seen
> limit
)
3186 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3189 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3190 || (!max_access
&& (uData
->access
< actor
->access
)))
3192 del_channel_user(uData
, 1);
3200 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3202 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3206 static CHANSERV_FUNC(cmd_trim
)
3208 unsigned long duration
;
3209 unsigned short min_level
, max_level
;
3214 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3215 duration
= ParseInterval(argv
[2]);
3218 reply("CSMSG_CANNOT_TRIM");
3222 if(!irccasecmp(argv
[1], "lamers"))
3224 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3227 else if(!irccasecmp(argv
[1], "users"))
3229 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3232 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3234 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3237 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3239 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3244 reply("CSMSG_INVALID_TRIM", argv
[1]);
3249 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3250 to the user. cmd_all takes advantage of this. */
3251 static CHANSERV_FUNC(cmd_up
)
3253 struct mod_chanmode change
;
3254 struct userData
*uData
;
3257 mod_chanmode_init(&change
);
3259 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3260 if(!change
.args
[0].u
.member
)
3263 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3267 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3271 reply("CSMSG_GODMODE_UP", argv
[0]);
3274 else if(uData
->access
>= UL_OP
)
3276 change
.args
[0].mode
= MODE_CHANOP
;
3277 errmsg
= "CSMSG_ALREADY_OPPED";
3279 else if(uData
->access
>= UL_HALFOP
)
3281 change
.args
[0].mode
= MODE_HALFOP
;
3282 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3284 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3286 change
.args
[0].mode
= MODE_VOICE
;
3287 errmsg
= "CSMSG_ALREADY_VOICED";
3292 reply("CSMSG_NO_ACCESS");
3295 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3296 if(!change
.args
[0].mode
)
3299 reply(errmsg
, channel
->name
);
3302 modcmd_chanmode_announce(&change
);
3306 static CHANSERV_FUNC(cmd_down
)
3308 struct mod_chanmode change
;
3310 mod_chanmode_init(&change
);
3312 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3313 if(!change
.args
[0].u
.member
)
3316 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3320 if(!change
.args
[0].u
.member
->modes
)
3323 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3327 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3328 modcmd_chanmode_announce(&change
);
3332 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
)
3334 struct userData
*cList
;
3336 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3338 if(IsSuspended(cList
->channel
)
3339 || IsUserSuspended(cList
)
3340 || !GetUserMode(cList
->channel
->channel
, user
))
3343 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3349 static CHANSERV_FUNC(cmd_upall
)
3351 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3354 static CHANSERV_FUNC(cmd_downall
)
3356 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3359 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3360 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3363 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
)
3365 unsigned int ii
, valid
;
3366 struct userNode
*victim
;
3367 struct mod_chanmode
*change
;
3369 change
= mod_chanmode_alloc(argc
- 1);
3371 for(ii
=valid
=0; ++ii
< argc
; )
3373 if(!(victim
= GetUserH(argv
[ii
])))
3375 change
->args
[valid
].mode
= mode
;
3376 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3377 if(!change
->args
[valid
].u
.member
)
3379 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3384 change
->argc
= valid
;
3385 if(valid
< (argc
-1))
3386 reply("CSMSG_PROCESS_FAILED");
3389 modcmd_chanmode_announce(change
);
3390 reply(action
, channel
->name
);
3392 mod_chanmode_free(change
);
3396 static CHANSERV_FUNC(cmd_op
)
3398 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3401 static CHANSERV_FUNC(cmd_hop
)
3403 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3406 static CHANSERV_FUNC(cmd_deop
)
3408 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3411 static CHANSERV_FUNC(cmd_dehop
)
3413 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3416 static CHANSERV_FUNC(cmd_voice
)
3418 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3421 static CHANSERV_FUNC(cmd_devoice
)
3423 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3427 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3433 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3435 struct modeNode
*mn
= channel
->members
.list
[ii
];
3437 if(IsService(mn
->user
))
3440 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3443 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3447 victims
[(*victimCount
)++] = mn
;
3453 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3455 struct userNode
*victim
;
3456 struct modeNode
**victims
;
3457 unsigned int offset
, n
, victimCount
, duration
= 0;
3458 char *reason
= "Bye.", *ban
, *name
;
3459 char interval
[INTERVALLEN
];
3461 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3462 REQUIRE_PARAMS(offset
);
3465 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3466 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3468 /* Truncate the reason to a length of TOPICLEN, as
3469 the ircd does; however, leave room for an ellipsis
3470 and the kicker's nick. */
3471 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3475 if((victim
= GetUserH(argv
[1])))
3477 victims
= alloca(sizeof(victims
[0]));
3478 victims
[0] = GetUserMode(channel
, victim
);
3479 /* XXX: The comparison with ACTION_KICK is just because all
3480 * other actions can work on users outside the channel, and we
3481 * want to allow those (e.g. unbans) in that case. If we add
3482 * some other ejection action for in-channel users, change
3484 victimCount
= victims
[0] ? 1 : 0;
3486 if(IsService(victim
))
3489 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3493 if((action
== ACTION_KICK
) && !victimCount
)
3496 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3500 if(protect_user(victim
, user
, channel
->channel_info
, false))
3502 // This translates to send_message(user, cmd->parent->bot, ...)
3503 // if user is x3 (ctcp action) cmd is null and segfault.
3505 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3509 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3510 name
= victim
->nick
;
3514 if(!is_ircmask(argv
[1]))
3517 reply("MSG_NICK_UNKNOWN", argv
[1]);
3521 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3523 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3526 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3529 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3530 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3532 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3533 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3534 some creativity, but its not x3's job to be the ban censor anyway. */
3535 if(is_overmask(argv
[1]))
3538 reply("CSMSG_LAME_MASK", argv
[1]);
3542 if((action
== ACTION_KICK
) && (victimCount
== 0))
3545 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3549 name
= ban
= strdup(argv
[1]);
3552 /* Truncate the ban in place if necessary; we must ensure
3553 that 'ban' is a valid ban mask before sanitizing it. */
3554 sanitize_ircmask(ban
);
3556 if(action
& ACTION_ADD_LAMER
)
3558 struct banData
*bData
, *next
;
3560 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3563 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3568 if(action
& ACTION_ADD_TIMED_LAMER
)
3570 duration
= ParseInterval(argv
[2]);
3575 reply("CSMSG_DURATION_TOO_LOW");
3579 else if(duration
> (86400 * 365 * 2))
3582 reply("CSMSG_DURATION_TOO_HIGH");
3589 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3591 if(match_ircglobs(bData
->mask
, ban
))
3593 int exact
= !irccasecmp(bData
->mask
, ban
);
3595 /* The ban is redundant; there is already a ban
3596 with the same effect in place. */
3600 free(bData
->reason
);
3601 bData
->reason
= strdup(reason
);
3602 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3604 reply("CSMSG_REASON_CHANGE", ban
);
3608 if(exact
&& bData
->expires
)
3612 /* If the ban matches an existing one exactly,
3613 extend the expiration time if the provided
3614 duration is longer. */
3615 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3617 bData
->expires
= now
+ duration
;
3628 /* Delete the expiration timeq entry and
3629 requeue if necessary. */
3630 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3633 timeq_add(bData
->expires
, expire_ban
, bData
);
3637 /* automated kickban, dont reply */
3640 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3642 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3648 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3655 if(match_ircglobs(ban
, bData
->mask
))
3657 /* The ban we are adding makes previously existing
3658 bans redundant; silently remove them. */
3659 del_channel_ban(bData
);
3663 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
);
3665 name
= ban
= strdup(bData
->mask
);
3669 /* WHAT DOES THIS DO?? -Rubin */
3670 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3672 extern const char *hidden_host_suffix
;
3673 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3675 unsigned int l1
, l2
;
3678 l2
= strlen(old_name
);
3681 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3683 new_mask
= malloc(MAXLEN
);
3684 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3686 name
= ban
= new_mask
;
3691 if(action
& ACTION_BAN
)
3693 unsigned int exists
;
3694 struct mod_chanmode
*change
;
3696 if(channel
->banlist
.used
>= MAXBANS
)
3699 reply("CSMSG_BANLIST_FULL", channel
->name
);
3704 exists
= ChannelBanExists(channel
, ban
);
3705 change
= mod_chanmode_alloc(victimCount
+ 1);
3706 for(n
= 0; n
< victimCount
; ++n
)
3708 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3709 change
->args
[n
].u
.member
= victims
[n
];
3713 change
->args
[n
].mode
= MODE_BAN
;
3714 change
->args
[n
++].u
.hostmask
= ban
;
3718 modcmd_chanmode_announce(change
);
3720 mod_chanmode_announce(chanserv
, channel
, change
);
3721 mod_chanmode_free(change
);
3723 if(exists
&& (action
== ACTION_BAN
))
3726 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3732 if(action
& ACTION_ADD_LAMER
)
3734 char kick_reason
[MAXLEN
];
3735 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3737 for(n
= 0; n
< victimCount
; n
++) {
3738 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3739 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3743 else if(action
& ACTION_KICK
)
3745 char kick_reason
[MAXLEN
];
3746 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3748 for(n
= 0; n
< victimCount
; n
++) {
3749 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3755 /* No response, since it was automated. */
3757 else if(action
& ACTION_ADD_LAMER
)
3760 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3762 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3764 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3765 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3766 else if(action
& ACTION_BAN
)
3767 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3768 else if(action
& ACTION_KICK
&& victimCount
)
3769 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3775 static CHANSERV_FUNC(cmd_kickban
)
3777 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3780 static CHANSERV_FUNC(cmd_kick
)
3782 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3785 static CHANSERV_FUNC(cmd_ban
)
3787 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3790 static CHANSERV_FUNC(cmd_addlamer
)
3792 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3795 static CHANSERV_FUNC(cmd_addtimedlamer
)
3797 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3800 static struct mod_chanmode
*
3801 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3803 struct mod_chanmode
*change
;
3804 unsigned char *match
;
3805 unsigned int ii
, count
;
3807 match
= alloca(bans
->used
);
3810 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3812 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3813 MATCH_USENICK
| MATCH_VISIBLE
);
3820 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3822 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3829 change
= mod_chanmode_alloc(count
);
3830 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3834 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3835 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3837 assert(count
== change
->argc
);
3841 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3843 unsigned int jj
, ii
, count
;
3845 struct chanData
*channel
;
3847 struct mod_chanmode
*change
;
3849 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3850 /* Walk through every channel */
3851 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3852 switch(channel
->chOpts
[chBanTimeout
])
3854 default: case '0': continue; /* Dont remove bans in this chan */
3855 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3856 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3857 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3858 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3859 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3862 /* First find out how many bans were going to unset */
3863 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3864 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3868 /* At least one ban, so setup a removal */
3869 change
= mod_chanmode_alloc(count
);
3871 /* Walk over every ban in this channel.. */
3872 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3873 bn
= channel
->channel
->banlist
.list
[jj
];
3874 if (bn
->set
< bantimeout
) {
3875 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3877 /* Add this ban to the mode change */
3878 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3879 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3881 /* Pull this ban out of the list */
3882 banList_remove(&(channel
->channel
->banlist
), bn
);
3887 /* Send the modes to IRC */
3888 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3890 /* free memory from strdup above */
3891 for(ii
= 0; ii
< count
; ++ii
)
3892 free((char*)change
->args
[ii
].u
.hostmask
);
3894 mod_chanmode_free(change
);
3897 /* Set this function to run again */
3898 if(chanserv_conf
.ban_timeout_frequency
)
3899 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3904 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3906 struct userNode
*actee
;
3912 /* may want to allow a comma delimited list of users... */
3913 if(!(actee
= GetUserH(argv
[1])))
3915 if(!is_ircmask(argv
[1]))
3917 reply("MSG_NICK_UNKNOWN", argv
[1]);
3921 mask
= strdup(argv
[1]);
3924 /* We don't sanitize the mask here because ircu
3926 if(action
& ACTION_UNBAN
)
3928 struct mod_chanmode
*change
;
3929 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3934 modcmd_chanmode_announce(change
);
3935 for(ii
= 0; ii
< change
->argc
; ++ii
)
3936 free((char*)change
->args
[ii
].u
.hostmask
);
3937 mod_chanmode_free(change
);
3942 if(action
& ACTION_DEL_LAMER
)
3944 struct banData
*ban
, *next
;
3946 ban
= channel
->channel_info
->bans
; /* lamers */
3950 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3953 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3958 del_channel_ban(ban
);
3965 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3967 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3973 static CHANSERV_FUNC(cmd_unban
)
3975 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3978 static CHANSERV_FUNC(cmd_dellamer
)
3980 /* it doesn't necessarily have to remove the channel ban - may want
3981 to make that an option. */
3982 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3985 static CHANSERV_FUNC(cmd_unbanme
)
3987 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3988 long flags
= ACTION_UNBAN
;
3990 /* remove permanent bans if the user has the proper access. */
3991 if(uData
->access
>= UL_MANAGER
)
3992 flags
|= ACTION_DEL_LAMER
;
3994 argv
[1] = user
->nick
;
3995 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3998 static CHANSERV_FUNC(cmd_unbanall
)
4000 struct mod_chanmode
*change
;
4003 if(!channel
->banlist
.used
)
4005 reply("CSMSG_NO_BANS", channel
->name
);
4009 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4010 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4012 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4013 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4015 modcmd_chanmode_announce(change
);
4016 for(ii
= 0; ii
< change
->argc
; ++ii
)
4017 free((char*)change
->args
[ii
].u
.hostmask
);
4018 mod_chanmode_free(change
);
4019 reply("CSMSG_BANS_REMOVED", channel
->name
);
4023 static CHANSERV_FUNC(cmd_open
)
4025 struct mod_chanmode
*change
;
4028 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4030 change
= mod_chanmode_alloc(0);
4031 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4032 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4033 && channel
->channel_info
->modes
.modes_set
)
4034 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4035 modcmd_chanmode_announce(change
);
4036 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4037 for(ii
= 0; ii
< change
->argc
; ++ii
)
4038 free((char*)change
->args
[ii
].u
.hostmask
);
4039 mod_chanmode_free(change
);
4043 static CHANSERV_FUNC(cmd_myaccess
)
4045 static struct string_buffer sbuf
;
4046 struct handle_info
*target_handle
;
4047 struct userData
*uData
;
4050 target_handle
= user
->handle_info
;
4051 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4053 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
4055 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4058 if(!target_handle
->channels
)
4060 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4064 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4065 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4067 struct chanData
*cData
= uData
->channel
;
4069 if(uData
->access
> UL_OWNER
)
4071 if(IsProtected(cData
)
4072 && (target_handle
!= user
->handle_info
)
4073 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4076 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4077 if(uData
->flags
== USER_AUTO_OP
)
4078 string_buffer_append(&sbuf
, ',');
4079 if(IsUserSuspended(uData
))
4080 string_buffer_append(&sbuf
, 's');
4081 if(IsUserAutoOp(uData
))
4083 if(uData
->access
>= UL_OP
)
4084 string_buffer_append(&sbuf
, 'o');
4085 else if(uData
->access
>= UL_HALFOP
)
4086 string_buffer_append(&sbuf
, 'h');
4087 else if(uData
->access
>= UL_PEON
)
4088 string_buffer_append(&sbuf
, 'v');
4090 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4091 string_buffer_append(&sbuf
, 'i');
4092 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4093 string_buffer_append(&sbuf
, 'j');
4095 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4097 string_buffer_append_string(&sbuf
, ")]");
4098 string_buffer_append(&sbuf
, '\0');
4099 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4105 static CHANSERV_FUNC(cmd_access
)
4107 struct userNode
*target
;
4108 struct handle_info
*target_handle
;
4109 struct userData
*uData
;
4111 char prefix
[MAXLEN
];
4116 target_handle
= target
->handle_info
;
4118 else if((target
= GetUserH(argv
[1])))
4120 target_handle
= target
->handle_info
;
4122 else if(argv
[1][0] == '*')
4124 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4126 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4132 reply("MSG_NICK_UNKNOWN", argv
[1]);
4136 assert(target
|| target_handle
);
4138 if(target
== chanserv
)
4140 reply("CSMSG_IS_CHANSERV");
4148 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4153 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4156 reply("MSG_AUTHENTICATE");
4162 const char *epithet
= NULL
, *type
= NULL
;
4165 epithet
= chanserv_conf
.irc_operator_epithet
;
4168 else if(IsNetworkHelper(target
))
4170 epithet
= chanserv_conf
.network_helper_epithet
;
4171 type
= "network helper";
4173 else if(IsSupportHelper(target
))
4175 epithet
= chanserv_conf
.support_helper_epithet
;
4176 type
= "support helper";
4180 if(target_handle
->epithet
)
4181 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4183 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4185 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4189 sprintf(prefix
, "%s", target_handle
->handle
);
4192 if(!channel
->channel_info
)
4194 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4198 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4199 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4200 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4202 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4203 /* To prevent possible information leaks, only show infolines
4204 * if the requestor is in the channel or it's their own
4206 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4208 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4210 /* Likewise, only say it's suspended if the user has active
4211 * access in that channel or it's their own entry. */
4212 if(IsUserSuspended(uData
)
4213 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4214 || (user
->handle_info
== uData
->handle
)))
4216 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4221 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4227 /* This is never used...
4229 zoot_list(struct listData *list)
4231 struct userData *uData;
4232 unsigned int start, curr, highest, lowest;
4233 struct helpfile_table tmp_table;
4234 const char **temp, *msg;
4236 if(list->table.length == 1)
4239 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);
4241 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));
4242 msg = user_find_message(list->user, "MSG_NONE");
4243 send_message_type(4, list->user, list->bot, " %s", msg);
4245 tmp_table.width = list->table.width;
4246 tmp_table.flags = list->table.flags;
4247 list->table.contents[0][0] = " ";
4248 highest = list->highest;
4249 if(list->lowest != 0)
4250 lowest = list->lowest;
4251 else if(highest < 100)
4254 lowest = highest - 100;
4255 for(start = curr = 1; curr < list->table.length; )
4257 uData = list->users[curr-1];
4258 list->table.contents[curr++][0] = " ";
4259 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4262 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);
4264 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));
4265 temp = list->table.contents[--start];
4266 list->table.contents[start] = list->table.contents[0];
4267 tmp_table.contents = list->table.contents + start;
4268 tmp_table.length = curr - start;
4269 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4270 list->table.contents[start] = temp;
4272 highest = lowest - 1;
4273 lowest = (highest < 100) ? 0 : (highest - 99);
4280 normal_list(struct listData
*list
)
4284 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
);
4286 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
));
4287 if(list
->table
.length
== 1)
4289 msg
= user_find_message(list
->user
, "MSG_NONE");
4290 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4293 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4296 /* if these need changed, uncomment and customize
4298 clean_list(struct listData *list)
4302 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);
4304 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));
4305 if(list->table.length == 1)
4307 msg = user_find_message(list->user, "MSG_NONE");
4308 send_message_type(4, list->user, list->bot, " %s", msg);
4311 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4315 advanced_list(struct listData *list)
4319 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);
4321 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));
4322 if(list->table.length == 1)
4324 msg = user_find_message(list->user, "MSG_NONE");
4325 send_message_type(4, list->user, list->bot, " %s", msg);
4328 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4332 classic_list(struct listData *list)
4336 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4338 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4339 if(list->table.length == 1)
4341 msg = user_find_message(list->user, "MSG_NONE");
4342 send_message_type(4, list->user, list->bot, " %s", msg);
4345 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4350 userData_access_comp(const void *arg_a
, const void *arg_b
)
4352 const struct userData
*a
= *(struct userData
**)arg_a
;
4353 const struct userData
*b
= *(struct userData
**)arg_b
;
4355 if(a
->access
!= b
->access
)
4356 res
= b
->access
- a
->access
;
4358 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4363 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4365 void (*send_list
)(struct listData
*);
4366 struct userData
*uData
;
4367 struct listData lData
;
4368 unsigned int matches
;
4374 lData
.bot
= cmd
->parent
->bot
;
4375 lData
.channel
= channel
;
4376 lData
.lowest
= lowest
;
4377 lData
.highest
= highest
;
4378 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4379 send_list
= normal_list
;
4380 /* What does the following line do exactly?? */
4381 /*(void)zoot_list; ** since it doesn't show user levels */
4384 if(user->handle_info)
4386 switch(user->handle_info->userlist_style)
4388 case HI_STYLE_CLEAN:
4389 send_list = clean_list;
4391 case HI_STYLE_ADVANCED:
4392 send_list = advanced_list;
4394 case HI_STYLE_CLASSIC:
4395 send_list = classic_list;
4397 case HI_STYLE_NORMAL:
4399 send_list = normal_list;
4404 send_list
= normal_list
;
4406 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4408 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4410 if((uData
->access
< lowest
)
4411 || (uData
->access
> highest
)
4412 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4414 lData
.users
[matches
++] = uData
;
4416 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4418 lData
.table
.length
= matches
+1;
4419 lData
.table
.flags
= TABLE_NO_FREE
;
4420 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4422 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4423 lData
.table
.width
= 6; /* with level = 6 */
4425 lData
.table
.width
= 5; /* without = 5 */
4426 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4427 lData
.table
.contents
[0] = ary
;
4428 if(user
->handle_info
) {
4429 switch(user
->handle_info
->userlist_style
) {
4430 case HI_STYLE_CLASSIC
:
4433 case HI_STYLE_ADVANCED
:
4434 ary
[i
++] = "Access";
4437 case HI_STYLE_CLEAN
:
4438 ary
[i
++] = "Access";
4440 case HI_STYLE_NORMAL
:
4442 ary
[i
++] = "Access";
4447 ary
[i
++] = "Access";
4449 ary
[i
++] = "Account";
4450 ary
[i
] = "Last Seen";
4452 ary
[i
++] = "Status";
4453 ary
[i
++] = "Expiry";
4454 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4456 struct userData
*uData
= lData
.users
[matches
-1];
4457 char seen
[INTERVALLEN
];
4460 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4461 lData
.table
.contents
[matches
] = ary
;
4462 if(user
->handle_info
) {
4463 switch(user
->handle_info
->userlist_style
) {
4464 case HI_STYLE_CLASSIC
:
4465 ary
[i
++] = strtab(uData
->access
);
4467 case HI_STYLE_ADVANCED
:
4468 ary
[i
++] = user_level_name_from_level(uData
->access
);
4469 ary
[i
++] = strtab(uData
->access
);
4471 case HI_STYLE_CLEAN
:
4472 ary
[i
++] = user_level_name_from_level(uData
->access
);
4474 case HI_STYLE_NORMAL
:
4476 ary
[i
++] = user_level_name_from_level(uData
->access
);
4481 ary
[i
++] = user_level_name_from_level(uData
->access
);
4483 ary
[i
++] = uData
->handle
->handle
;
4486 else if(!uData
->seen
)
4489 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4490 ary
[i
] = strdup(ary
[i
]);
4492 if(IsUserSuspended(uData
))
4493 ary
[i
++] = "Suspended";
4494 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4495 ary
[i
++] = "Vacation";
4497 ary
[i
++] = "Normal";
4499 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4500 char delay
[INTERVALLEN
];
4503 if (uData
->accessexpiry
> 0) {
4504 diff
= uData
->accessexpiry
- now
;
4505 intervalString(delay
, diff
, user
->handle_info
);
4507 diff
= uData
->clvlexpiry
- now
;
4508 intervalString(delay
, diff
, user
->handle_info
);
4516 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4518 /* Free strdup above */
4519 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4520 free(lData
.table
.contents
[matches
]);
4522 free(lData
.table
.contents
[0]);
4523 free(lData
.table
.contents
);
4527 /* Remove this now that debugging is over? or improve it for
4528 * users? Would it be better tied into USERS somehow? -Rubin */
4529 static CHANSERV_FUNC(cmd_pending
)
4531 struct adduserPending
*ap
;
4532 reply("CSMSG_ADDUSER_PENDING_HEADER");
4533 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4535 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4536 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4537 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4541 static CHANSERV_FUNC(cmd_users
)
4543 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4546 static CHANSERV_FUNC(cmd_wlist
)
4548 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4551 static CHANSERV_FUNC(cmd_clist
)
4553 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4556 static CHANSERV_FUNC(cmd_mlist
)
4558 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4561 static CHANSERV_FUNC(cmd_olist
)
4563 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4566 static CHANSERV_FUNC(cmd_hlist
)
4568 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4571 static CHANSERV_FUNC(cmd_plist
)
4573 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4576 static CHANSERV_FUNC(cmd_lamers
)
4578 struct helpfile_table tbl
;
4579 unsigned int matches
= 0, timed
= 0, ii
;
4580 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4581 const char *msg_never
, *triggered
, *expires
;
4582 struct banData
*ban
, **bans
; /* lamers */
4589 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4590 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4593 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4595 if(search
&& !match_ircglobs(search
, ban
->mask
))
4597 bans
[matches
++] = ban
;
4602 tbl
.length
= matches
+ 1;
4603 tbl
.width
= 4 + timed
;
4605 tbl
.flags
= TABLE_NO_FREE
;
4606 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4607 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4608 tbl
.contents
[0][0] = "Mask";
4609 tbl
.contents
[0][1] = "Set By";
4610 tbl
.contents
[0][2] = "Triggered";
4613 tbl
.contents
[0][3] = "Expires";
4614 tbl
.contents
[0][4] = "Reason";
4617 tbl
.contents
[0][3] = "Reason";
4620 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4621 /* reply("MSG_NONE"); */
4622 free(tbl
.contents
[0]);
4627 msg_never
= user_find_message(user
, "MSG_NEVER");
4628 for(ii
= 0; ii
< matches
; )
4634 else if(ban
->expires
)
4635 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4637 expires
= msg_never
;
4640 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4642 triggered
= msg_never
;
4644 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4645 tbl
.contents
[ii
][0] = ban
->mask
;
4646 tbl
.contents
[ii
][1] = ban
->owner
;
4647 tbl
.contents
[ii
][2] = strdup(triggered
);
4650 tbl
.contents
[ii
][3] = strdup(expires
);
4651 tbl
.contents
[ii
][4] = ban
->reason
;
4654 tbl
.contents
[ii
][3] = ban
->reason
;
4656 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4657 /* reply("MSG_MATCH_COUNT", matches); */
4658 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4660 free((char*)tbl
.contents
[ii
][2]);
4662 free((char*)tbl
.contents
[ii
][3]);
4663 free(tbl
.contents
[ii
]);
4665 free(tbl
.contents
[0]);
4672 * return + if the user does NOT have the right to set the topic, and
4673 * the topic is changed.
4676 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4678 struct chanData
*cData
= channel
->channel_info
;
4679 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4681 else if(cData
->topic
)
4682 return irccasecmp(new_topic
, cData
->topic
);
4689 * Makes a givin topic fit into a givin topic mask and returns
4692 * topic_mask - the mask to conform to
4693 * topic - the topic to make conform
4694 * new_topic - the pre-allocated char* to put the new topic into
4696 * modifies: new_topic
4699 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4701 //char *topic_mask = cData->topic_mask;
4703 int pos
=0, starpos
=-1, dpos
=0, len
;
4705 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4712 strcpy(new_topic
, "");
4715 len
= strlen(topic
);
4716 if((dpos
+ len
) > TOPICLEN
)
4717 len
= TOPICLEN
+ 1 - dpos
;
4718 memcpy(new_topic
+dpos
, topic
, len
);
4722 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4723 default: new_topic
[dpos
++] = tchar
; break;
4726 if((dpos
> TOPICLEN
) || tchar
)
4728 strcpy(new_topic
, "");
4731 new_topic
[dpos
] = 0;
4735 static CHANSERV_FUNC(cmd_topic
)
4737 struct chanData
*cData
;
4741 #ifdef WITH_PROTOCOL_P10
4745 cData
= channel
->channel_info
;
4750 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4751 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4752 reply("CSMSG_TOPIC_SET", cData
->topic
);
4756 reply("CSMSG_NO_TOPIC", channel
->name
);
4760 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4761 /* If they say "!topic *", use an empty topic. */
4762 if((topic
[0] == '*') && (topic
[1] == 0))
4765 if(bad_topic(channel
, user
, topic
))
4767 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4772 /* If there is a topicmask set, and the new topic doesnt match, make it */
4773 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4775 char *topic_mask
= cData
->topic_mask
;
4776 char new_topic
[TOPICLEN
+1];
4778 /* make a new topic fitting mask */
4779 conform_topic(topic_mask
, topic
, new_topic
);
4782 /* Topic couldnt fit into mask, was too long */
4783 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4784 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4787 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4789 else /* No mask set, just set the topic */
4790 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4793 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4795 /* Grab the topic and save it as the default topic. */
4797 cData
->topic
= strdup(channel
->topic
);
4803 static CHANSERV_FUNC(cmd_mode
)
4805 struct userData
*uData
;
4806 struct mod_chanmode
*change
;
4811 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4812 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4816 change
= &channel
->channel_info
->modes
;
4817 if(change
->modes_set
|| change
->modes_clear
) {
4818 modcmd_chanmode_announce(change
);
4819 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4821 reply("CSMSG_NO_MODES", channel
->name
);
4825 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4827 base_oplevel
= MAXOPLEVEL
;
4828 else if (uData
->access
>= UL_OWNER
)
4831 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4832 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4836 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4840 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4841 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4844 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4845 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4849 modcmd_chanmode_announce(change
);
4850 mod_chanmode_free(change
);
4851 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4855 static CHANSERV_FUNC(cmd_invite
)
4857 struct userData
*uData
;
4858 struct userNode
*invite
;
4860 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4864 if(!(invite
= GetUserH(argv
[1])))
4866 reply("MSG_NICK_UNKNOWN", argv
[1]);
4873 if(GetUserMode(channel
, invite
))
4875 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4883 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4884 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4887 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4890 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4892 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4893 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4894 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4900 irc_invite(chanserv
, invite
, channel
);
4902 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4907 static CHANSERV_FUNC(cmd_inviteme
)
4909 if(GetUserMode(channel
, user
))
4911 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4914 if(channel
->channel_info
4915 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4917 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4920 irc_invite(cmd
->parent
->bot
, user
, channel
);
4925 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4928 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4930 /* We display things based on two dimensions:
4931 * - Issue time: present or absent
4932 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4933 * (in order of precedence, so something both expired and revoked
4934 * only counts as revoked)
4936 combo
= (suspended
->issued
? 4 : 0)
4937 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4939 case 0: /* no issue time, indefinite expiration */
4940 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4942 case 1: /* no issue time, expires in future */
4943 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4944 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4946 case 2: /* no issue time, expired */
4947 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4948 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4950 case 3: /* no issue time, revoked */
4951 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4952 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4954 case 4: /* issue time set, indefinite expiration */
4955 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4956 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4958 case 5: /* issue time set, expires in future */
4959 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4960 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4961 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4963 case 6: /* issue time set, expired */
4964 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4965 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4966 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4968 case 7: /* issue time set, revoked */
4969 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4970 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4971 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4974 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4980 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4983 const char *fmt
= "%a %b %d %H:%M %Y";
4984 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4986 if(giveownership
->staff_issuer
)
4988 if(giveownership
->reason
)
4989 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4990 giveownership
->target
, giveownership
->target_access
,
4991 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4993 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4994 giveownership
->target
, giveownership
->target_access
,
4995 giveownership
->staff_issuer
, buf
);
4999 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5004 static CHANSERV_FUNC(cmd_info
)
5006 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5007 struct userData
*uData
, *owner
;
5008 struct chanData
*cData
;
5009 struct do_not_register
*dnr
;
5014 cData
= channel
->channel_info
;
5015 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5016 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5019 uData
= GetChannelUser(cData
, user
->handle_info
);
5020 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5022 mod_chanmode_format(&cData
->modes
, modes
);
5023 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5024 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5027 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5031 note
= iter_data(it
);
5032 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5035 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5036 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5039 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5040 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5041 if(owner
->access
== UL_OWNER
)
5042 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5043 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5044 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5045 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5047 privileged
= IsStaff(user
);
5048 /* if(privileged) */
5049 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5050 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5051 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5053 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5054 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5056 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5058 struct suspended
*suspended
;
5059 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5060 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5061 show_suspension_info(cmd
, user
, suspended
);
5063 else if(IsSuspended(cData
))
5065 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5066 show_suspension_info(cmd
, user
, cData
->suspended
);
5068 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5070 struct giveownership
*giveownership
;
5071 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5072 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5073 show_giveownership_info(cmd
, user
, giveownership
);
5075 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5076 reply("CSMSG_CHANNEL_END");
5078 reply("CSMSG_CHANNEL_END_CLEAN");
5082 static CHANSERV_FUNC(cmd_netinfo
)
5084 extern time_t boot_time
;
5085 extern unsigned long burst_length
;
5086 char interval
[INTERVALLEN
];
5088 reply("CSMSG_NETWORK_INFO");
5089 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5090 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5091 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5092 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5093 reply("CSMSG_NETWORK_LAMERS", banCount
);
5094 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5095 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5096 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5101 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5103 struct helpfile_table table
;
5105 struct userNode
*user
;
5110 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5111 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5112 for(nn
=0; nn
<list
->used
; nn
++)
5114 user
= list
->list
[nn
];
5115 if(user
->modes
& skip_flags
)
5119 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5122 nick
= alloca(strlen(user
->nick
)+3);
5123 sprintf(nick
, "(%s)", user
->nick
);
5127 table
.contents
[table
.length
][0] = nick
;
5130 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5133 static CHANSERV_FUNC(cmd_ircops
)
5135 reply("CSMSG_STAFF_OPERS");
5136 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5140 static CHANSERV_FUNC(cmd_helpers
)
5142 reply("CSMSG_STAFF_HELPERS");
5143 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5147 static CHANSERV_FUNC(cmd_staff
)
5149 reply("CSMSG_NETWORK_STAFF");
5150 cmd_ircops(CSFUNC_ARGS
);
5151 cmd_helpers(CSFUNC_ARGS
);
5155 static CHANSERV_FUNC(cmd_peek
)
5157 struct modeNode
*mn
;
5158 char modes
[MODELEN
];
5160 struct helpfile_table table
;
5162 irc_make_chanmode(channel
, modes
);
5164 reply("CSMSG_PEEK_INFO", channel
->name
);
5165 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5167 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5168 reply("CSMSG_PEEK_MODES", modes
);
5169 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5173 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5174 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5175 for(n
= 0; n
< channel
->members
.used
; n
++)
5177 mn
= channel
->members
.list
[n
];
5178 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5180 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5181 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5186 reply("CSMSG_PEEK_OPS");
5187 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5190 reply("CSMSG_PEEK_NO_OPS");
5191 reply("CSMSG_PEEK_END");
5195 static MODCMD_FUNC(cmd_wipeinfo
)
5197 struct handle_info
*victim
;
5198 struct userData
*ud
, *actor
;
5201 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5202 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5204 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5206 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5209 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5211 reply("MSG_USER_OUTRANKED", victim
->handle
);
5217 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5222 resync_channel(struct chanNode
*channel
)
5224 struct mod_chanmode
*changes
;
5225 struct chanData
*cData
= channel
->channel_info
;
5226 unsigned int ii
, used
;
5228 /* 6 = worst case -ovh+ovh on everyone */
5229 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5230 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5232 struct modeNode
*mn
= channel
->members
.list
[ii
];
5233 struct userData
*uData
;
5235 if(IsService(mn
->user
))
5239 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5241 /* If the channel is in no-mode mode, de-mode EVERYONE */
5242 if(cData
->chOpts
[chAutomode
] == 'n')
5246 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5247 changes
->args
[used
++].u
.member
= mn
;
5250 else /* Give various userlevels their modes.. */
5252 if(uData
&& uData
->access
>= UL_OP
)
5254 if(!(mn
->modes
& MODE_CHANOP
))
5256 changes
->args
[used
].mode
= MODE_CHANOP
;
5257 changes
->args
[used
++].u
.member
= mn
;
5260 else if(uData
&& uData
->access
>= UL_HALFOP
)
5262 if(mn
->modes
& MODE_CHANOP
)
5264 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5265 changes
->args
[used
++].u
.member
= mn
;
5267 if(!(mn
->modes
& MODE_HALFOP
))
5269 changes
->args
[used
].mode
= MODE_HALFOP
;
5270 changes
->args
[used
++].u
.member
= mn
;
5273 else if(uData
&& uData
->access
>= UL_PEON
)
5275 if(mn
->modes
& MODE_CHANOP
)
5277 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5278 changes
->args
[used
++].u
.member
= mn
;
5280 if(mn
->modes
& MODE_HALFOP
)
5282 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5283 changes
->args
[used
++].u
.member
= mn
;
5285 /* Don't voice peons if were in mode m */
5286 if( cData
->chOpts
[chAutomode
] == 'm')
5288 if(mn
->modes
& MODE_VOICE
)
5290 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5291 changes
->args
[used
++].u
.member
= mn
;
5294 /* otherwise, make user they do have voice */
5295 else if(!(mn
->modes
& MODE_VOICE
))
5297 changes
->args
[used
].mode
= MODE_VOICE
;
5298 changes
->args
[used
++].u
.member
= mn
;
5301 else /* They arnt on the userlist.. */
5303 /* If we voice everyone, but they dont.. */
5304 if(cData
->chOpts
[chAutomode
] == 'v')
5306 /* Remove anything except v */
5307 if(mn
->modes
& ~MODE_VOICE
)
5309 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5310 changes
->args
[used
++].u
.member
= mn
;
5313 if(!(mn
->modes
& MODE_VOICE
))
5315 changes
->args
[used
].mode
= MODE_VOICE
;
5316 changes
->args
[used
++].u
.member
= mn
;
5319 /* If we hop everyone, but they dont.. */
5320 else if(cData
->chOpts
[chAutomode
] == 'h')
5322 /* Remove anything except h */
5323 if(mn
->modes
& ~MODE_HALFOP
)
5325 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5326 changes
->args
[used
++].u
.member
= mn
;
5329 if(!(mn
->modes
& MODE_HALFOP
))
5331 changes
->args
[used
].mode
= MODE_HALFOP
;
5332 changes
->args
[used
++].u
.member
= mn
;
5335 /* If we op everyone, but they dont.. */
5336 else if(cData
->chOpts
[chAutomode
] == 'o')
5338 /* Remove anything except h */
5339 if(mn
->modes
& ~MODE_CHANOP
)
5341 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5342 changes
->args
[used
++].u
.member
= mn
;
5345 if(!(mn
->modes
& MODE_CHANOP
))
5347 changes
->args
[used
].mode
= MODE_CHANOP
;
5348 changes
->args
[used
++].u
.member
= mn
;
5351 /* they have no excuse for having modes, de-everything them */
5356 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5357 changes
->args
[used
++].u
.member
= mn
;
5363 changes
->argc
= used
;
5364 mod_chanmode_announce(chanserv
, channel
, changes
);
5365 mod_chanmode_free(changes
);
5368 static CHANSERV_FUNC(cmd_resync
)
5370 resync_channel(channel
);
5371 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5375 static CHANSERV_FUNC(cmd_seen
)
5377 struct userData
*uData
;
5378 struct handle_info
*handle
;
5379 char seen
[INTERVALLEN
];
5383 if(!irccasecmp(argv
[1], chanserv
->nick
))
5385 reply("CSMSG_IS_CHANSERV");
5389 if(!(handle
= get_handle_info(argv
[1])))
5391 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5395 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5397 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5402 reply("CSMSG_USER_PRESENT", handle
->handle
);
5403 else if(uData
->seen
)
5404 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5406 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5408 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5409 reply("CSMSG_USER_VACATION", handle
->handle
);
5414 static MODCMD_FUNC(cmd_names
)
5416 struct userNode
*targ
;
5417 struct userData
*targData
;
5418 unsigned int ii
, pos
;
5421 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5423 targ
= channel
->members
.list
[ii
]->user
;
5424 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5427 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5430 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5434 if(IsUserSuspended(targData
))
5436 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5439 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5440 reply("CSMSG_END_NAMES", channel
->name
);
5445 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5447 switch(ntype
->visible_type
)
5449 case NOTE_VIS_ALL
: return 1;
5450 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5451 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5456 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5458 struct userData
*uData
;
5460 switch(ntype
->set_access_type
)
5462 case NOTE_SET_CHANNEL_ACCESS
:
5463 if(!user
->handle_info
)
5465 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5467 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5468 case NOTE_SET_CHANNEL_SETTER
:
5469 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5470 case NOTE_SET_PRIVILEGED
: default:
5471 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5475 static CHANSERV_FUNC(cmd_note
)
5477 struct chanData
*cData
;
5479 struct note_type
*ntype
;
5481 cData
= channel
->channel_info
;
5484 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5488 /* If no arguments, show all visible notes for the channel. */
5494 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5496 note
= iter_data(it
);
5497 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5500 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5501 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5504 reply("CSMSG_NOTELIST_END", channel
->name
);
5506 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5508 /* If one argument, show the named note. */
5511 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5512 && note_type_visible_to_user(cData
, note
->type
, user
))
5514 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5516 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5517 && note_type_visible_to_user(NULL
, ntype
, user
))
5519 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5524 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5528 /* Assume they're trying to set a note. */
5532 ntype
= dict_find(note_types
, argv
[1], NULL
);
5535 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5538 else if(note_type_settable_by_user(channel
, ntype
, user
))
5540 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5541 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5542 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5543 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5544 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5546 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5548 /* The note is viewable to staff only, so return 0
5549 to keep the invocation from getting logged (or
5550 regular users can see it in !events). */
5556 reply("CSMSG_NO_ACCESS");
5563 static CHANSERV_FUNC(cmd_delnote
)
5568 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5569 || !note_type_settable_by_user(channel
, note
->type
, user
))
5571 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5574 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5575 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5579 static CHANSERV_FUNC(cmd_last
)
5585 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5587 if(numoflines
< 1 || numoflines
> 200)
5589 reply("CSMSG_LAST_INVALID");
5592 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5596 static CHANSERV_FUNC(cmd_events
)
5598 struct logSearch discrim
;
5599 struct logReport report
;
5600 unsigned int matches
, limit
;
5602 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5603 if(limit
< 1 || limit
> 200)
5606 memset(&discrim
, 0, sizeof(discrim
));
5607 discrim
.masks
.bot
= chanserv
;
5608 discrim
.masks
.channel_name
= channel
->name
;
5610 discrim
.masks
.command
= argv
[2];
5611 discrim
.limit
= limit
;
5612 discrim
.max_time
= INT_MAX
;
5613 discrim
.severities
= 1 << LOG_COMMAND
;
5614 report
.reporter
= chanserv
;
5616 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5617 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5619 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5621 reply("MSG_MATCH_COUNT", matches
);
5623 reply("MSG_NO_MATCHES");
5627 static CHANSERV_FUNC(cmd_say
)
5633 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5634 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5636 else if(GetUserH(argv
[1]))
5639 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5640 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5644 reply("MSG_NOT_TARGET_NAME");
5650 static CHANSERV_FUNC(cmd_emote
)
5656 /* CTCP is so annoying. */
5657 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5658 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5660 else if(GetUserH(argv
[1]))
5662 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5663 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5667 reply("MSG_NOT_TARGET_NAME");
5673 struct channelList
*
5674 chanserv_support_channels(void)
5676 return &chanserv_conf
.support_channels
;
5679 static CHANSERV_FUNC(cmd_expire
)
5681 int channel_count
= registered_channels
;
5682 expire_channels(NULL
);
5683 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5688 chanserv_expire_suspension(void *data
)
5690 struct suspended
*suspended
= data
;
5691 struct chanNode
*channel
;
5693 if(!suspended
->expires
|| (now
< suspended
->expires
))
5694 suspended
->revoked
= now
;
5695 channel
= suspended
->cData
->channel
;
5696 suspended
->cData
->channel
= channel
;
5697 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5698 if(!IsOffChannel(suspended
->cData
))
5700 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5701 ss_cs_join_channel(channel
, 1);
5705 static CHANSERV_FUNC(cmd_csuspend
)
5707 struct suspended
*suspended
;
5708 char reason
[MAXLEN
];
5709 time_t expiry
, duration
;
5710 struct userData
*uData
;
5714 if(IsProtected(channel
->channel_info
))
5716 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5720 if(argv
[1][0] == '!')
5722 else if(IsSuspended(channel
->channel_info
))
5724 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5725 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5729 if(!strcmp(argv
[1], "0"))
5731 else if((duration
= ParseInterval(argv
[1])))
5732 expiry
= now
+ duration
;
5735 reply("MSG_INVALID_DURATION", argv
[1]);
5739 unsplit_string(argv
+ 2, argc
- 2, reason
);
5741 suspended
= calloc(1, sizeof(*suspended
));
5742 suspended
->revoked
= 0;
5743 suspended
->issued
= now
;
5744 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5745 suspended
->expires
= expiry
;
5746 suspended
->reason
= strdup(reason
);
5747 suspended
->cData
= channel
->channel_info
;
5748 suspended
->previous
= suspended
->cData
->suspended
;
5749 suspended
->cData
->suspended
= suspended
;
5751 if(suspended
->expires
)
5752 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5754 if(IsSuspended(channel
->channel_info
))
5756 suspended
->previous
->revoked
= now
;
5757 if(suspended
->previous
->expires
)
5758 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5760 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5761 channel
->name
, suspended
->suspender
);
5765 /* Mark all users in channel as absent. */
5766 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5775 /* Mark the channel as suspended, then part. */
5776 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5777 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5778 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5779 reply("CSMSG_SUSPENDED", channel
->name
);
5780 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5781 channel
->name
, suspended
->suspender
);
5786 static CHANSERV_FUNC(cmd_cunsuspend
)
5788 struct suspended
*suspended
;
5790 if(!IsSuspended(channel
->channel_info
))
5792 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5796 suspended
= channel
->channel_info
->suspended
;
5798 /* Expire the suspension and join ChanServ to the channel. */
5799 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5800 chanserv_expire_suspension(suspended
);
5801 reply("CSMSG_UNSUSPENDED", channel
->name
);
5802 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5803 channel
->name
, user
->handle_info
->handle
);
5807 typedef struct chanservSearch
5815 unsigned long flags
;
5819 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5822 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5827 search
= malloc(sizeof(struct chanservSearch
));
5828 memset(search
, 0, sizeof(*search
));
5831 for(i
= 0; i
< argc
; i
++)
5833 /* Assume all criteria require arguments. */
5836 reply("MSG_MISSING_PARAMS", argv
[i
]);
5840 if(!irccasecmp(argv
[i
], "name"))
5841 search
->name
= argv
[++i
];
5842 else if(!irccasecmp(argv
[i
], "registrar"))
5843 search
->registrar
= argv
[++i
];
5844 else if(!irccasecmp(argv
[i
], "unvisited"))
5845 search
->unvisited
= ParseInterval(argv
[++i
]);
5846 else if(!irccasecmp(argv
[i
], "registered"))
5847 search
->registered
= ParseInterval(argv
[++i
]);
5848 else if(!irccasecmp(argv
[i
], "flags"))
5851 if(!irccasecmp(argv
[i
], "nodelete"))
5852 search
->flags
|= CHANNEL_NODELETE
;
5853 else if(!irccasecmp(argv
[i
], "suspended"))
5854 search
->flags
|= CHANNEL_SUSPENDED
;
5857 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5861 else if(!irccasecmp(argv
[i
], "limit"))
5862 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5865 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5870 if(search
->name
&& !strcmp(search
->name
, "*"))
5872 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5873 search
->registrar
= 0;
5882 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5884 const char *name
= channel
->channel
->name
;
5885 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5886 (search
->registrar
&& !channel
->registrar
) ||
5887 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5888 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5889 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5890 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5897 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5899 struct chanData
*channel
;
5900 unsigned int matches
= 0;
5902 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5904 if(!chanserv_channel_match(channel
, search
))
5914 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5919 search_print(struct chanData
*channel
, void *data
)
5921 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5924 static CHANSERV_FUNC(cmd_search
)
5927 unsigned int matches
;
5928 channel_search_func action
;
5932 if(!irccasecmp(argv
[1], "count"))
5933 action
= search_count
;
5934 else if(!irccasecmp(argv
[1], "print"))
5935 action
= search_print
;
5938 reply("CSMSG_ACTION_INVALID", argv
[1]);
5942 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5946 if(action
== search_count
)
5947 search
->limit
= INT_MAX
;
5949 if(action
== search_print
)
5951 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5952 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5956 matches
= chanserv_channel_search(search
, action
, user
);
5959 reply("MSG_MATCH_COUNT", matches
);
5961 reply("MSG_NO_MATCHES");
5967 static CHANSERV_FUNC(cmd_unvisited
)
5969 struct chanData
*cData
;
5970 time_t interval
= chanserv_conf
.channel_expire_delay
;
5971 char buffer
[INTERVALLEN
];
5972 unsigned int limit
= 25, matches
= 0;
5976 interval
= ParseInterval(argv
[1]);
5978 limit
= atoi(argv
[2]);
5981 intervalString(buffer
, interval
, user
->handle_info
);
5982 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5984 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5986 if((now
- cData
->visited
) < interval
)
5989 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5990 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5997 static MODCMD_FUNC(chan_opt_defaulttopic
)
6003 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6005 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6009 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6011 free(channel
->channel_info
->topic
);
6012 if(topic
[0] == '*' && topic
[1] == 0)
6014 topic
= channel
->channel_info
->topic
= NULL
;
6018 topic
= channel
->channel_info
->topic
= strdup(topic
);
6019 if(channel
->channel_info
->topic_mask
6020 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6021 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6023 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6026 if(channel
->channel_info
->topic
)
6027 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6029 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6033 static MODCMD_FUNC(chan_opt_topicmask
)
6037 struct chanData
*cData
= channel
->channel_info
;
6040 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6042 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6046 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6048 if(cData
->topic_mask
)
6049 free(cData
->topic_mask
);
6050 if(mask
[0] == '*' && mask
[1] == 0)
6052 cData
->topic_mask
= 0;
6056 cData
->topic_mask
= strdup(mask
);
6058 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6059 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6060 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6064 if(channel
->channel_info
->topic_mask
)
6065 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6067 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6071 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6075 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6079 if(greeting
[0] == '*' && greeting
[1] == 0)
6083 unsigned int length
= strlen(greeting
);
6084 if(length
> chanserv_conf
.greeting_length
)
6086 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6089 *data
= strdup(greeting
);
6098 reply(name
, user_find_message(user
, "MSG_NONE"));
6102 static MODCMD_FUNC(chan_opt_greeting
)
6104 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6107 static MODCMD_FUNC(chan_opt_usergreeting
)
6109 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6112 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6114 unsigned int charmax
;
6117 charmax
= atoi(argv
[1]);
6118 if ((charmax
> 0) && (charmax
< chanserv_conf
.max_userinfo_length
))
6119 channel
->channel_info
->maxsetinfo
= charmax
;
6122 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6126 static MODCMD_FUNC(chan_opt_modes
)
6128 struct mod_chanmode
*new_modes
;
6129 char modes
[MODELEN
];
6133 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6134 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6138 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6140 reply("CSMSG_NO_ACCESS");
6143 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6145 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6147 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6149 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6152 else if(new_modes
->argc
> 1)
6154 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6155 mod_chanmode_free(new_modes
);
6160 channel
->channel_info
->modes
= *new_modes
;
6161 modcmd_chanmode_announce(new_modes
);
6162 mod_chanmode_free(new_modes
);
6166 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6168 reply("CSMSG_SET_MODES", modes
);
6170 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6174 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6176 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6178 struct chanData
*cData
= channel
->channel_info
;
6183 /* Set flag according to value. */
6184 if(enabled_string(argv
[1]))
6186 cData
->flags
|= mask
;
6189 else if(disabled_string(argv
[1]))
6191 cData
->flags
&= ~mask
;
6196 reply("MSG_INVALID_BINARY", argv
[1]);
6202 /* Find current option value. */
6203 value
= (cData
->flags
& mask
) ? 1 : 0;
6207 reply(name
, user_find_message(user
, "MSG_ON"));
6209 reply(name
, user_find_message(user
, "MSG_OFF"));
6213 static MODCMD_FUNC(chan_opt_nodelete
)
6215 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6217 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6221 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6224 static MODCMD_FUNC(chan_opt_dynlimit
)
6226 struct mod_chanmode change
;
6229 if (disabled_string(argv
[1])) {
6230 mod_chanmode_init(&change
);
6231 change
.modes_clear
|= MODE_LIMIT
;
6232 mod_chanmode_announce(chanserv
, channel
, &change
);
6236 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6239 static MODCMD_FUNC(chan_opt_offchannel
)
6241 struct chanData
*cData
= channel
->channel_info
;
6246 /* Set flag according to value. */
6247 if(enabled_string(argv
[1]))
6249 if(!IsOffChannel(cData
))
6250 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6251 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6254 else if(disabled_string(argv
[1]))
6256 if(IsOffChannel(cData
))
6258 struct mod_chanmode change
;
6259 mod_chanmode_init(&change
);
6261 change
.args
[0].mode
= MODE_CHANOP
;
6262 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6263 mod_chanmode_announce(chanserv
, channel
, &change
);
6265 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6270 reply("MSG_INVALID_BINARY", argv
[1]);
6276 /* Find current option value. */
6277 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6281 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6283 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6287 static MODCMD_FUNC(chan_opt_defaults
)
6289 struct userData
*uData
;
6290 struct chanData
*cData
;
6291 const char *confirm
;
6292 enum levelOption lvlOpt
;
6293 enum charOption chOpt
;
6295 cData
= channel
->channel_info
;
6296 uData
= GetChannelUser(cData
, user
->handle_info
);
6297 if(!uData
|| (uData
->access
< UL_OWNER
))
6299 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6302 confirm
= make_confirmation_string(uData
);
6303 if((argc
< 2) || strcmp(argv
[1], confirm
))
6305 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6308 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6309 cData
->modes
= chanserv_conf
.default_modes
;
6310 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6311 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6312 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6313 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6314 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6319 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6321 struct chanData
*cData
= channel
->channel_info
;
6322 struct userData
*uData
;
6323 unsigned short value
;
6327 if(!check_user_level(channel
, user
, option
, 1, 1))
6329 reply("CSMSG_CANNOT_SET");
6332 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6333 if(!value
&& strcmp(argv
[1], "0"))
6335 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6338 uData
= GetChannelUser(cData
, user
->handle_info
);
6339 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6341 reply("CSMSG_BAD_SETLEVEL");
6347 /* This test only applies to owners, since non-owners
6348 * trying to set an option to above their level get caught
6349 * by the CSMSG_BAD_SETLEVEL test above.
6351 if(value
> uData
->access
)
6353 reply("CSMSG_BAD_SETTERS");
6360 cData
->lvlOpts
[option
] = value
;
6362 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6366 static MODCMD_FUNC(chan_opt_enfops
)
6368 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6371 static MODCMD_FUNC(chan_opt_enfhalfops
)
6373 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6375 static MODCMD_FUNC(chan_opt_enfmodes
)
6377 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6380 static MODCMD_FUNC(chan_opt_enftopic
)
6382 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6385 static MODCMD_FUNC(chan_opt_pubcmd
)
6387 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6390 static MODCMD_FUNC(chan_opt_setters
)
6392 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6395 static MODCMD_FUNC(chan_opt_userinfo
)
6397 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6400 static MODCMD_FUNC(chan_opt_topicsnarf
)
6402 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6405 static MODCMD_FUNC(chan_opt_inviteme
)
6407 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6410 /* TODO: Make look like this when no args are
6412 * -X3- -------------------------------
6413 * -X3- BanTimeout: Bans are removed:
6414 * -X3- ----- * indicates current -----
6415 * -X3- 0: [*] Never.
6416 * -X3- 1: [ ] After 10 minutes.
6417 * -X3- 2: [ ] After 2 hours.
6418 * -X3- 3: [ ] After 4 hours.
6419 * -X3- 4: [ ] After 24 hours.
6420 * -X3- 5: [ ] After one week.
6421 * -X3- ------------- End -------------
6424 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6426 struct chanData
*cData
= channel
->channel_info
;
6427 int count
= charOptions
[option
].count
, index
;
6431 index
= atoi(argv
[1]);
6433 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6435 reply("CSMSG_INVALID_NUMERIC", argv
[1]);
6436 /* Show possible values. */
6437 for(index
= 0; index
< count
; index
++)
6438 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6442 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6446 /* Find current option value. */
6449 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6453 /* Somehow, the option value is corrupt; reset it to the default. */
6454 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6459 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6463 static MODCMD_FUNC(chan_opt_automode
)
6465 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6468 static MODCMD_FUNC(chan_opt_protect
)
6470 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6473 static MODCMD_FUNC(chan_opt_toys
)
6475 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6478 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6480 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6483 static MODCMD_FUNC(chan_opt_bantimeout
)
6485 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6488 static MODCMD_FUNC(chan_opt_topicrefresh
)
6490 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6493 static MODCMD_FUNC(chan_opt_resync
)
6495 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6498 static struct svccmd_list set_shows_list
;
6501 handle_svccmd_unbind(struct svccmd
*target
) {
6503 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6504 if(target
== set_shows_list
.list
[ii
])
6505 set_shows_list
.used
= 0;
6508 static CHANSERV_FUNC(cmd_set
)
6510 struct svccmd
*subcmd
;
6514 /* Check if we need to (re-)initialize set_shows_list. */
6515 if(!set_shows_list
.used
)
6517 if(!set_shows_list
.size
)
6519 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6520 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6522 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6524 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6525 sprintf(buf
, "%s %s", argv
[0], name
);
6526 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6529 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6532 svccmd_list_append(&set_shows_list
, subcmd
);
6538 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6539 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6541 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6543 subcmd
= set_shows_list
.list
[ii
];
6544 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6546 reply("CSMSG_CHANNEL_OPTIONS_END");
6550 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6551 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6554 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6557 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6559 reply("CSMSG_NO_ACCESS");
6563 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6567 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6569 struct userData
*uData
;
6571 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6574 reply("CSMSG_NOT_USER", channel
->name
);
6580 /* Just show current option value. */
6582 else if(enabled_string(argv
[1]))
6584 uData
->flags
|= mask
;
6586 else if(disabled_string(argv
[1]))
6588 uData
->flags
&= ~mask
;
6592 reply("MSG_INVALID_BINARY", argv
[1]);
6596 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6600 static MODCMD_FUNC(user_opt_autoop
)
6602 struct userData
*uData
;
6604 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6607 reply("CSMSG_NOT_USER", channel
->name
);
6610 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6611 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6613 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6616 static MODCMD_FUNC(user_opt_autoinvite
)
6618 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6621 static MODCMD_FUNC(user_opt_autojoin
)
6623 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6626 static MODCMD_FUNC(user_opt_info
)
6628 struct userData
*uData
;
6631 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6635 /* If they got past the command restrictions (which require access)
6636 * but fail this test, we have some fool with security override on.
6638 reply("CSMSG_NOT_USER", channel
->name
);
6645 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6646 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
6648 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
6651 bp
= strcspn(infoline
, "\001");
6654 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6659 if(infoline
[0] == '*' && infoline
[1] == 0)
6662 uData
->info
= strdup(infoline
);
6665 reply("CSMSG_USET_INFO", uData
->info
);
6667 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6671 struct svccmd_list uset_shows_list
;
6673 static CHANSERV_FUNC(cmd_uset
)
6675 struct svccmd
*subcmd
;
6679 /* Check if we need to (re-)initialize uset_shows_list. */
6680 if(!uset_shows_list
.used
)
6684 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6687 if(!uset_shows_list
.size
)
6689 uset_shows_list
.size
= ArrayLength(options
);
6690 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6692 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6694 const char *name
= options
[ii
];
6695 sprintf(buf
, "%s %s", argv
[0], name
);
6696 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6699 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6702 svccmd_list_append(&uset_shows_list
, subcmd
);
6708 /* Do this so options are presented in a consistent order. */
6709 reply("CSMSG_USER_OPTIONS");
6710 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6711 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6715 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6716 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6719 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6723 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6726 static CHANSERV_FUNC(cmd_giveownership
)
6728 struct handle_info
*new_owner_hi
;
6729 struct userData
*new_owner
, *curr_user
;
6730 struct chanData
*cData
= channel
->channel_info
;
6731 struct do_not_register
*dnr
;
6732 struct giveownership
*giveownership
;
6733 unsigned int force
, override
;
6734 unsigned short co_access
, new_owner_old_access
;
6735 char transfer_reason
[MAXLEN
];
6738 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6739 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6741 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6742 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6743 && (uData
->access
> 500)
6744 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6745 || uData
->access
< 500));
6748 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6750 struct userData
*owner
= NULL
;
6751 for(curr_user
= channel
->channel_info
->users
;
6753 curr_user
= curr_user
->next
)
6755 if(curr_user
->access
!= UL_OWNER
)
6759 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6766 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6768 char delay
[INTERVALLEN
];
6769 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6770 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6774 reply("CSMSG_NO_OWNER", channel
->name
);
6777 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6779 if(new_owner_hi
== user
->handle_info
)
6781 reply("CSMSG_NO_TRANSFER_SELF");
6784 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6789 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
, 0);
6793 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6797 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6799 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6802 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6803 if(!IsHelping(user
))
6804 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6806 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6810 new_owner_old_access
= new_owner
->access
;
6811 if(new_owner
->access
>= UL_COOWNER
)
6812 co_access
= new_owner
->access
;
6814 co_access
= UL_COOWNER
;
6815 new_owner
->access
= UL_OWNER
;
6817 curr_user
->access
= co_access
;
6818 cData
->ownerTransfer
= now
;
6820 giveownership
= calloc(1, sizeof(*giveownership
));
6821 giveownership
->issued
= now
;
6822 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
6823 giveownership
->target
= strdup(new_owner_hi
->handle
);
6824 giveownership
->target_access
= new_owner_old_access
;
6827 if(argc
> (2 + force
))
6829 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6830 giveownership
->reason
= strdup(transfer_reason
);
6832 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6835 giveownership
->previous
= channel
->channel_info
->giveownership
;
6836 channel
->channel_info
->giveownership
= giveownership
;
6838 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6839 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6840 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6845 chanserv_expire_user_suspension(void *data
)
6847 struct userData
*target
= data
;
6849 target
->expires
= 0;
6850 target
->flags
&= ~USER_SUSPENDED
;
6853 static CHANSERV_FUNC(cmd_suspend
)
6855 struct handle_info
*hi
;
6856 struct userData
*self
, *target
;
6860 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6861 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6862 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6864 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6867 if(target
->access
>= self
->access
)
6869 reply("MSG_USER_OUTRANKED", hi
->handle
);
6872 if(target
->flags
& USER_SUSPENDED
)
6874 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6879 target
->present
= 0;
6882 if(!strcmp(argv
[2], "0"))
6886 unsigned int duration
;
6887 if(!(duration
= ParseInterval(argv
[2])))
6889 reply("MSG_INVALID_DURATION", argv
[2]);
6892 expiry
= now
+ duration
;
6895 target
->expires
= expiry
;
6898 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6900 target
->flags
|= USER_SUSPENDED
;
6901 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6905 static CHANSERV_FUNC(cmd_unsuspend
)
6907 struct handle_info
*hi
;
6908 struct userData
*self
, *target
;
6911 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6912 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6913 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6915 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6918 if(target
->access
>= self
->access
)
6920 reply("MSG_USER_OUTRANKED", hi
->handle
);
6923 if(!(target
->flags
& USER_SUSPENDED
))
6925 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6928 target
->flags
&= ~USER_SUSPENDED
;
6929 scan_user_presence(target
, NULL
);
6930 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6931 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6935 static MODCMD_FUNC(cmd_deleteme
)
6937 struct handle_info
*hi
;
6938 struct userData
*target
;
6939 const char *confirm_string
;
6940 unsigned short access
;
6943 hi
= user
->handle_info
;
6944 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6946 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6949 if(target
->access
== UL_OWNER
)
6951 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6954 confirm_string
= make_confirmation_string(target
);
6955 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6957 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6960 access
= target
->access
;
6961 channel_name
= strdup(channel
->name
);
6962 del_channel_user(target
, 1);
6963 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6969 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6971 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6972 struct chanData
*cData
;
6975 for(cData
= channelList
; cData
; cData
= cData
->next
)
6977 if(IsSuspended(cData
))
6979 opt
= cData
->chOpts
[chTopicRefresh
];
6982 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6985 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6986 cData
->last_refresh
= refresh_num
;
6988 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6992 chanserv_auto_resync(UNUSED_ARG(void *data
))
6994 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6995 struct chanData
*cData
;
6998 for(cData
= channelList
; cData
; cData
= cData
->next
)
7000 if(IsSuspended(cData
)) continue;
7001 opt
= cData
->chOpts
[chResync
];
7002 if(opt
== 'n') continue;
7003 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
7004 resync_channel(cData
->channel
);
7005 cData
->last_resync
= refresh_num
;
7007 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7010 static CHANSERV_FUNC(cmd_unf
)
7014 char response
[MAXLEN
];
7015 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7016 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7017 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7020 reply("CSMSG_UNF_RESPONSE");
7024 static CHANSERV_FUNC(cmd_ping
)
7028 char response
[MAXLEN
];
7029 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7030 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7031 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7034 reply("CSMSG_PING_RESPONSE");
7038 static CHANSERV_FUNC(cmd_wut
)
7042 char response
[MAXLEN
];
7043 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7044 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7045 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7048 reply("CSMSG_WUT_RESPONSE");
7052 static CHANSERV_FUNC(cmd_roulette
)
7055 struct chanData
*cData
= channel
->channel_info
;
7058 if (cData
->roulette_chamber
) {
7059 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7063 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7064 cData
->roulette_chamber
= 1 + rand() % 6;
7070 static CHANSERV_FUNC(cmd_shoot
)
7073 struct chanData
*cData
= channel
->channel_info
;
7075 if (cData
->roulette_chamber
<= 0) {
7076 struct service
*service
;
7077 if ((service
= service_find(chanserv
->nick
))) {
7078 reply("CSMSG_ROULETTE_NEW", service
->trigger
);
7083 cData
->roulette_chamber
--;
7085 if (cData
->roulette_chamber
== 0) {
7086 reply("CSMSG_ROULETTE_BANG");
7087 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7088 DelUser(user
, chanserv
, 1, "BANG!!!!");
7090 reply("CSMSG_ROULETTE_CLICK");
7097 chanserv_remove_abuse(void *data
)
7099 char *remnick
= data
;
7100 struct userNode
*user
;
7101 /* sometimes the clone was killed and maybe even the user took their nick back
7102 * (ie, an oper) so dont kill them here after all unless they are local. */
7103 if( (user
= GetUserH(remnick
)) )
7105 DelUser(user
, NULL
, 1, "");
7108 int lamepart(struct userNode
*nick
) {
7109 struct modeNode
*mn
;
7110 unsigned int count
, n
;
7112 for (n
=count
=0; n
<nick
->channels
.used
; n
++) {
7113 mn
= nick
->channels
.list
[n
];
7114 irc_svspart(chanserv
, nick
, mn
->channel
);
7120 static CHANSERV_FUNC(cmd_spin
)
7125 int type
= 0, lamep
= 1;
7128 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7136 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7137 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7138 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7140 if(chanserv_conf
.wheel
->used
< 1) {
7141 /* wheel actions not defined! eek */
7145 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7146 if(!wheel
&& *wheel
)
7149 /* enable this to be able to manually specify a result for testing:
7150 log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
7156 /* connection reset by peer */
7157 if (!strcasecmp(wheel
, "peer")) {
7158 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7160 irc_kill(chanserv
, user
, "Connection reset by peer");
7162 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7164 /* part all channels */
7165 else if (!strcasecmp(wheel
, "partall")) {
7166 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7170 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7172 /* random time gline */
7173 else if (!strcasecmp(wheel
, "gline")) {
7174 char target
[IRC_NTOP_MAX_SIZE
+ 3];
7175 int wtime
= 120 + rand() % 600;
7177 strcpy(target
, "*@");
7178 strcat(target
, user
->hostname
);
7179 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7181 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1, 0);
7182 // irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7185 else if (!strcasecmp(wheel
, "shun")) {
7186 char target
[IRC_NTOP_MAX_SIZE
+ 3];
7187 int wtime
= 120 + rand() % 600;
7189 strcpy(target
, "*@");
7190 strcat(target
, user
->hostname
);
7191 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SHUN");
7193 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1);
7195 /* absolutely nothing */
7196 else if (!strcasecmp(wheel
, "nothing")) {
7197 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7199 /* join random chans and part em several times */
7200 else if (!strcasecmp(wheel
, "randjoin")) {
7206 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7207 while(complete
!= 1) {
7208 if (rndchans
!= 15) {
7209 chango
= 120 + rand() % 600;
7210 sputsock("%s SJ %s #%d "FMT_TIME_T
, self
->numeric
, user
->numeric
, chango
, now
);
7213 if (roundz0r
!= 1) {
7217 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7224 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7230 /* abuse line added to /whois */
7231 else if (!strcasecmp(wheel
, "abusewhois")) {
7232 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7233 irc_swhois(chanserv
, user
, "is being defecated on by services");
7235 /* kick from each channel your in */
7236 else if (!strcasecmp(wheel
, "kickall")) {
7237 unsigned int count
, n
;
7238 struct modeNode
*mn
;
7240 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7242 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7243 mn
= user
->channels
.list
[n
];
7244 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7247 /* random nick change */
7248 else if (!strcasecmp(wheel
, "nickchange")) {
7249 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7251 char *oldnick
= NULL
;
7252 char *oldident
= NULL
;
7253 char *oldhost
= NULL
;
7254 char abusednick
[NICKLEN
] = "";
7255 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7256 struct userNode
*clone
;
7258 oldnick
= strdup(user
->nick
);
7259 oldident
= strdup(user
->ident
);
7260 oldhost
= strdup(user
->hostname
);
7262 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7264 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7265 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7266 if(!GetUserH(abusednick
))
7270 SVSNickChange(user
, abusednick
);
7271 irc_svsnick(chanserv
, user
, abusednick
);
7273 clone
= AddClone(oldnick
, oldident
, oldhost
, "I got abused by the wheel of misfortune :D");
7274 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7277 else if (!strcasecmp(wheel
, "kill")) {
7278 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7280 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7281 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7283 /* service ignore */
7284 else if (!strcasecmp(wheel
, "svsignore")) {
7285 int gagged
, ignoretime
= 0;
7286 char target
[IRC_NTOP_MAX_SIZE
+ 13];
7289 /* we cant gag opers, so just verbally abuse them */
7290 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7293 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7295 strcpy(target
, "*!*@");
7296 strcat(target
, user
->hostname
);
7297 ignoretime
= now
+ (1 + rand() % 120);
7299 gagged
= gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7301 /* kick and ban from each channel your in */
7302 else if (!strcasecmp(wheel
, "kickbanall")) {
7303 unsigned int count
, n
;
7304 struct modeNode
*mn
;
7305 //char ban[IRC_NTOP_MAX_SIZE + 1];
7307 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7309 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7310 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7311 struct mod_chanmode
*change
;
7312 /* struct banData *bData; */
7313 unsigned int exists
;
7314 /* int duration = 300; */
7317 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7319 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7320 mn
= user
->channels
.list
[n
];
7321 if(mn
->channel
->banlist
.used
>= MAXBANS
) {
7322 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7327 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7329 change
= mod_chanmode_alloc(1);
7330 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7331 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7334 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7335 mod_chanmode_free(change
);
7337 exists
= ChannelBanExists(mn
->channel
, ban
);
7339 change
= mod_chanmode_alloc(1);
7340 change
->args
[0].mode
= MODE_BAN
;
7341 change
->args
[0].u
.hostmask
= ban
;
7343 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7344 mod_chanmode_free(change
);
7348 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7352 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7356 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7363 static CHANSERV_FUNC(cmd_8ball
)
7365 unsigned int i
, j
, accum
;
7370 for(i
=1; i
<argc
; i
++)
7371 for(j
=0; argv
[i
][j
]; j
++)
7372 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7373 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7376 char response
[MAXLEN
];
7377 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7378 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7381 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7385 #else /* Use cool 8ball instead */
7387 void eightball(char *outcome
, int method
, unsigned int seed
)
7391 #define NUMOFCOLORS 18
7392 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7393 "white", "black", "grey", "brown",
7394 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7395 "fuchsia","turquoise","magenta", "cyan"};
7396 #define NUMOFLOCATIONS 50
7397 char balllocations
[50][55] = {
7398 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7399 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7400 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7401 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7402 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7403 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7404 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7405 "your bra", "your hair", "your bed", "the couch", "the wall",
7406 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7407 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7408 #define NUMOFPREPS 15
7409 char ballpreps
[50][50] = {
7410 "Near", "Somewhere near", "In", "In", "In",
7411 "In", "Hiding in", "Under", "Next to", "Over",
7412 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7413 #define NUMOFNUMS 34
7414 char ballnums
[50][50] = {
7415 "A hundred", "A thousand", "A few", "42",
7416 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7417 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7418 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7420 #define NUMOFMULTS 8
7421 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7424 * 0: normal (Not used in x3)
7431 if (method
== 1) /* A Color */
7435 answer
= (rand() % 12); /* Make sure this is the # of entries */
7438 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
7440 case 1: strcpy(tmp
, "Sort of a light %s color.");
7442 case 2: strcpy(tmp
, "Dark and dreary %s.");
7444 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
7446 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
7448 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
7450 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
7452 case 10: strcpy(tmp
, "Solid %s.");
7454 case 11: strcpy(tmp
, "Transparent %s.");
7456 default: strcpy(outcome
, "An invalid random number was generated.");
7459 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
7462 else if (method
== 2) /* Location */
7464 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
7466 else if (method
== 3) /* Number of ___ */
7468 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
7472 //Debug(DBGWARNING, "Error in 8ball.");
7477 static CHANSERV_FUNC(cmd_8ball
)
7479 char *word1
, *word2
, *word3
;
7480 static char eb
[MAXLEN
];
7481 unsigned int accum
, i
, j
;
7485 for(i
=1; i
<argc
; i
++)
7486 for(j
=0; argv
[i
][j
]; j
++)
7487 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7489 accum
+= time(NULL
)/3600;
7491 word2
= argc
>2?argv
[2]:"";
7492 word3
= argc
>3?argv
[3]:"";
7495 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
7496 eightball(eb
, 1, accum
);
7497 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7498 eightball(eb
, 1, accum
);
7499 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7500 eightball(eb
, 1, accum
);
7501 /*** LOCATION *****/
7506 (strcasecmp(word1
, "where") == 0) &&
7507 (strcasecmp(word2
, "is") == 0)
7511 strcasecmp(word1
, "where's") == 0
7514 eightball(eb
, 2, accum
);
7516 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
7517 eightball(eb
, 3, accum
);
7521 /* Generic 8ball question.. so pull from x3.conf srvx style */
7524 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7527 char response
[MAXLEN
];
7528 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
7529 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7532 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7538 char response
[MAXLEN
];
7539 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
7540 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7543 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
7548 static CHANSERV_FUNC(cmd_d
)
7550 unsigned long sides
, count
, modifier
, ii
, total
;
7551 char response
[MAXLEN
], *sep
;
7555 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
7565 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
7566 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
7570 else if((sep
[0] == '-') && isdigit(sep
[1]))
7571 modifier
= strtoul(sep
, NULL
, 10);
7572 else if((sep
[0] == '+') && isdigit(sep
[1]))
7573 modifier
= strtoul(sep
+1, NULL
, 10);
7580 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
7585 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7588 for(total
= ii
= 0; ii
< count
; ++ii
)
7589 total
+= (rand() % sides
) + 1;
7592 if((count
> 1) || modifier
)
7594 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7595 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7599 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7600 sprintf(response
, fmt
, total
, sides
);
7603 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7605 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7609 static CHANSERV_FUNC(cmd_huggle
)
7611 /* CTCP must be via PRIVMSG, never notice */
7613 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7615 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7619 static CHANSERV_FUNC(cmd_calc
)
7621 char response
[MAXLEN
];
7624 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7627 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7629 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7633 static CHANSERV_FUNC(cmd_reply
)
7637 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7640 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7642 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7647 chanserv_adjust_limit(void *data
)
7649 struct mod_chanmode change
;
7650 struct chanData
*cData
= data
;
7651 struct chanNode
*channel
= cData
->channel
;
7654 if(IsSuspended(cData
))
7657 cData
->limitAdjusted
= now
;
7658 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7659 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7661 if(limit
> cData
->modes
.new_limit
)
7662 limit
= cData
->modes
.new_limit
;
7663 else if(limit
== cData
->modes
.new_limit
)
7667 mod_chanmode_init(&change
);
7668 change
.modes_set
= MODE_LIMIT
;
7669 change
.new_limit
= limit
;
7670 mod_chanmode_announce(chanserv
, channel
, &change
);
7674 handle_new_channel(struct chanNode
*channel
)
7676 struct chanData
*cData
;
7678 if(!(cData
= channel
->channel_info
))
7681 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7682 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7684 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7685 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7689 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
7691 struct banData
*bData
;
7692 struct mod_chanmode
*change
;
7694 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
7699 if (chan
->channel_info
) {
7700 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
7702 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
))
7714 check_bans(struct userNode
*user
, const char *channel
)
7716 struct chanNode
*chan
;
7717 struct mod_chanmode change
;
7718 struct chanData
*cData
;
7719 struct banData
*bData
;
7721 if (!(chan
= GetChannel(channel
)))
7724 if(!(cData
= chan
->channel_info
))
7727 mod_chanmode_init(&change
);
7730 if(chan
->banlist
.used
< MAXBANS
)
7732 /* Not joining through a ban. */
7733 for(bData
= cData
->bans
;
7734 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7735 bData
= bData
->next
);
7739 char kick_reason
[MAXLEN
];
7740 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7742 bData
->triggered
= now
;
7743 if(bData
!= cData
->bans
)
7745 /* Shuffle the ban to the head of the list. */
7747 bData
->next
->prev
= bData
->prev
;
7749 bData
->prev
->next
= bData
->next
;
7752 bData
->next
= cData
->bans
;
7755 cData
->bans
->prev
= bData
;
7757 cData
->bans
= bData
;
7760 change
.args
[0].mode
= MODE_BAN
;
7761 change
.args
[0].u
.hostmask
= bData
->mask
;
7762 mod_chanmode_announce(chanserv
, chan
, &change
);
7763 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
7771 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
7774 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
7776 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
))
7783 /* Welcome to my worst nightmare. Warning: Read (or modify)
7784 the code below at your own risk. */
7786 handle_join(struct modeNode
*mNode
)
7788 struct mod_chanmode change
;
7789 struct userNode
*user
= mNode
->user
;
7790 struct chanNode
*channel
= mNode
->channel
;
7791 struct chanData
*cData
;
7792 struct userData
*uData
= NULL
;
7793 struct banData
*bData
;
7794 struct handle_info
*handle
;
7795 unsigned int modes
= 0, info
= 0;
7798 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7801 cData
= channel
->channel_info
;
7802 if(channel
->members
.used
> cData
->max
)
7803 cData
->max
= channel
->members
.used
;
7806 /* Check for bans. If they're joining through a ban, one of two
7808 * 1: Join during a netburst, by riding the break. Kick them
7809 * unless they have ops or voice in the channel.
7810 * 2: They're allowed to join through the ban (an invite in
7811 * ircu2.10, or a +e on Hybrid, or something).
7812 * If they're not joining through a ban, and the banlist is not
7813 * full, see if they're on the banlist for the channel. If so,
7816 if(user
->uplink
->burst
&& !mNode
->modes
)
7819 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7821 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7823 /* Riding a netburst. Naughty. */
7824 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7831 if(user
->handle_info
)
7833 handle
= user
->handle_info
;
7836 uData
= GetTrueChannelAccess(cData
, handle
);
7841 mod_chanmode_init(&change
);
7844 /* TODO: maybe only people above inviteme level? -Rubin */
7845 /* We don't kick people with access */
7846 if(!uData
&& !channel_user_is_exempt(user
, channel
))
7848 if(channel
->banlist
.used
< MAXBANS
)
7850 /* Not joining through a ban. */
7851 for(bData
= cData
->bans
;
7852 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7853 bData
= bData
->next
);
7857 char kick_reason
[MAXLEN
];
7858 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7860 bData
->triggered
= now
;
7861 if(bData
!= cData
->bans
)
7863 /* Shuffle the ban to the head of the list. */
7865 bData
->next
->prev
= bData
->prev
;
7867 bData
->prev
->next
= bData
->next
;
7870 bData
->next
= cData
->bans
;
7873 cData
->bans
->prev
= bData
;
7874 cData
->bans
= bData
;
7877 change
.args
[0].mode
= MODE_BAN
;
7878 change
.args
[0].u
.hostmask
= bData
->mask
;
7879 mod_chanmode_announce(chanserv
, channel
, &change
);
7880 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7886 /* ChanServ will not modify the limits in join-flooded channels.
7887 It will also skip DynLimit processing when the user (or srvx)
7888 is bursting in, because there are likely more incoming. */
7889 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7890 && !user
->uplink
->burst
7891 && !channel
->join_flooded
7892 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7894 /* The user count has begun "bumping" into the channel limit,
7895 so set a timer to raise the limit a bit. Any previous
7896 timers are removed so three incoming users within the delay
7897 results in one limit change, not three. */
7899 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7900 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7903 /* Give automodes exept during join-floods */
7904 if(!channel
->join_flooded
)
7906 if(cData
->chOpts
[chAutomode
] == 'v')
7907 modes
|= MODE_VOICE
;
7908 else if(cData
->chOpts
[chAutomode
] == 'h')
7909 modes
|= MODE_HALFOP
;
7910 else if(cData
->chOpts
[chAutomode
] == 'o')
7911 modes
|= MODE_CHANOP
;
7914 greeting
= cData
->greeting
;
7915 if(user
->handle_info
)
7917 /* handle = user->handle_info; */
7919 if(IsHelper(user
) && !IsHelping(user
))
7922 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7924 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7926 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7932 /* uData = GetTrueChannelAccess(cData, handle); */
7933 if(uData
&& !IsUserSuspended(uData
))
7935 /* non users getting automodes are handled above. */
7936 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7938 /* just op everyone with access */
7939 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7940 modes
|= MODE_VOICE
;
7941 /* or do their access level */
7942 else if(uData
->access
>= UL_OP
)
7943 modes
|= MODE_CHANOP
;
7944 else if(uData
->access
>= UL_HALFOP
)
7945 modes
|= MODE_HALFOP
;
7946 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7947 modes
|= MODE_VOICE
;
7949 if(uData
->access
>= UL_PRESENT
)
7950 cData
->visited
= now
;
7951 if(cData
->user_greeting
)
7952 greeting
= cData
->user_greeting
;
7954 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7955 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7963 /* If user joining normally (not during burst), apply op or voice,
7964 * and send greeting/userinfo as appropriate.
7966 if(!user
->uplink
->burst
)
7970 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7971 if(modes & MODE_CHANOP) {
7972 modes &= ~MODE_HALFOP;
7973 modes &= ~MODE_VOICE;
7976 change
.args
[0].mode
= modes
;
7977 change
.args
[0].u
.member
= mNode
;
7978 mod_chanmode_announce(chanserv
, channel
, &change
);
7981 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7983 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7989 chanserv_autojoin_channels(struct userNode
*user
)
7991 struct userData
*channel
;
7993 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7995 struct chanNode
*cn
;
7996 struct modeNode
*mn
;
7998 if(IsUserSuspended(channel
)
7999 || IsSuspended(channel
->channel
)
8000 || !(cn
= channel
->channel
->channel
))
8003 mn
= GetUserMode(cn
, user
);
8006 if(!IsUserSuspended(channel
)
8007 && IsUserAutoJoin(channel
)
8008 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8010 && !user
->uplink
->burst
)
8011 irc_svsjoin(chanserv
, user
, cn
);
8017 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
8019 struct mod_chanmode change
;
8020 struct userData
*channel
;
8021 unsigned int ii
, jj
, i
;
8023 if(!user
->handle_info
)
8026 mod_chanmode_init(&change
);
8028 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8030 struct chanNode
*cn
;
8031 struct chanData
*cData
;
8032 struct modeNode
*mn
;
8033 if(IsUserSuspended(channel
)
8034 || IsSuspended(channel
->channel
)
8035 || !(cn
= channel
->channel
->channel
))
8038 cData
= cn
->channel_info
;
8039 mn
= GetUserMode(cn
, user
);
8042 if(!IsUserSuspended(channel
)
8043 && IsUserAutoInvite(channel
)
8044 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8046 && !user
->uplink
->burst
)
8047 irc_invite(chanserv
, user
, cn
);
8051 if(channel
->access
>= UL_PRESENT
)
8052 channel
->channel
->visited
= now
;
8054 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8056 if(channel
->access
>= UL_OP
)
8057 change
.args
[0].mode
= MODE_CHANOP
;
8058 else if(channel
->access
>= UL_HALFOP
)
8059 change
.args
[0].mode
= MODE_HALFOP
;
8060 else if(channel
->access
>= UL_PEON
)
8061 change
.args
[0].mode
= MODE_VOICE
;
8063 change
.args
[0].mode
= 0;
8064 change
.args
[0].u
.member
= mn
;
8065 if(change
.args
[0].mode
)
8066 mod_chanmode_announce(chanserv
, cn
, &change
);
8069 channel
->seen
= now
;
8070 channel
->present
= 1;
8073 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8075 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
8076 struct banData
*ban
;
8078 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8079 || !channel
->channel_info
8080 || IsSuspended(channel
->channel_info
))
8082 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8084 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8085 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8087 if(jj
< channel
->banlist
.used
)
8089 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
8091 char kick_reason
[MAXLEN
];
8092 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
8094 change
.args
[0].mode
= MODE_BAN
;
8095 change
.args
[0].u
.hostmask
= ban
->mask
;
8096 mod_chanmode_announce(chanserv
, channel
, &change
);
8097 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8098 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8099 ban
->triggered
= now
;
8104 if(IsSupportHelper(user
))
8106 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8108 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8110 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8116 if (user
->handle_info
->ignores
->used
) {
8117 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8118 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8122 if (user
->handle_info
->epithet
)
8123 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8125 /* process autojoin channels 5 seconds later as this sometimes
8126 happens before autohide */
8127 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8128 chanserv_autojoin_channels(user
);
8132 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
8134 struct chanData
*cData
;
8135 struct userData
*uData
;
8137 cData
= mn
->channel
->channel_info
;
8138 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8141 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8143 /* Allow for a bit of padding so that the limit doesn't
8144 track the user count exactly, which could get annoying. */
8145 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8147 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8148 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8152 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8154 scan_user_presence(uData
, mn
->user
);
8158 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8160 unsigned int ii
, jj
;
8161 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8163 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
8164 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
8166 if(jj
< mn
->user
->channels
.used
)
8169 if(ii
== chanserv_conf
.support_channels
.used
)
8170 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8175 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
8177 struct userData
*uData
;
8179 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8180 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8181 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8184 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8186 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8187 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8190 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8195 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
8197 struct chanData
*cData
;
8199 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8202 cData
= channel
->channel_info
;
8203 if(bad_topic(channel
, user
, channel
->topic
))
8204 { /* User doesnt have privs to set topics. Undo it */
8205 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8206 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8209 /* If there is a topic mask set, and the new topic doesnt match,
8210 * set the topic to mask + new_topic */
8211 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8213 char new_topic
[TOPICLEN
+1];
8214 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8217 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8218 /* and fall through to topicsnarf code below.. */
8220 else /* Topic couldnt fit into mask, was too long */
8222 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8223 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8224 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8228 /* With topicsnarf, grab the topic and save it as the default topic. */
8229 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8232 cData
->topic
= strdup(channel
->topic
);
8238 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
8240 struct mod_chanmode
*bounce
= NULL
;
8241 unsigned int bnc
, ii
;
8244 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8247 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8248 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8250 char correct
[MAXLEN
];
8251 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8252 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8253 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8255 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8257 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8259 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8260 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8263 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8266 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8267 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8268 if(bounce
->args
[bnc
].u
.member
)
8272 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8273 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8275 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8277 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8279 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8280 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8283 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8284 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8285 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8288 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8290 const char *ban
= change
->args
[ii
].u
.hostmask
;
8291 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8294 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8295 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8296 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8298 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8303 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8304 mod_chanmode_announce(chanserv
, channel
, bounce
);
8305 for(ii
= 0; ii
< change
->argc
; ++ii
)
8306 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8307 free((char*)bounce
->args
[ii
].u
.hostmask
);
8308 mod_chanmode_free(bounce
);
8313 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
8315 struct chanNode
*channel
;
8316 struct banData
*bData
;
8317 struct mod_chanmode change
;
8318 unsigned int ii
, jj
;
8319 char kick_reason
[MAXLEN
];
8321 mod_chanmode_init(&change
);
8323 change
.args
[0].mode
= MODE_BAN
;
8324 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8326 channel
= user
->channels
.list
[ii
]->channel
;
8327 /* Need not check for bans if they're opped or voiced. */
8328 /* TODO: does this make sense in automode v, h, and o? *
8329 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8330 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8332 /* Need not check for bans unless channel registration is active. */
8333 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8335 /* Look for a matching ban already on the channel. */
8336 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8337 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8339 /* Need not act if we found one. */
8340 if(jj
< channel
->banlist
.used
)
8342 /* don't kick someone on the userlist */
8343 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8345 /* Look for a matching ban in this channel. */
8346 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8348 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
8350 change
.args
[0].u
.hostmask
= bData
->mask
;
8351 mod_chanmode_announce(chanserv
, channel
, &change
);
8352 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8353 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8354 bData
->triggered
= now
;
8355 break; /* we don't need to check any more bans in the channel */
8360 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
8362 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8366 dict_remove2(handle_dnrs
, old_handle
, 1);
8367 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8368 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8373 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
8375 struct userNode
*h_user
;
8377 if(handle
->channels
)
8379 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8380 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8382 while(handle
->channels
)
8383 del_channel_user(handle
->channels
, 1);
8388 handle_server_link(UNUSED_ARG(struct server
*server
))
8390 struct chanData
*cData
;
8392 for(cData
= channelList
; cData
; cData
= cData
->next
)
8394 if(!IsSuspended(cData
))
8395 cData
->may_opchan
= 1;
8396 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8397 && !cData
->channel
->join_flooded
8398 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8399 < chanserv_conf
.adjust_threshold
))
8401 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8402 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8408 chanserv_conf_read(void)
8412 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
8413 struct mod_chanmode
*change
;
8414 struct string_list
*strlist
;
8415 struct chanNode
*chan
;
8418 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
8420 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
8423 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8424 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8425 chanserv_conf
.support_channels
.used
= 0;
8426 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
8428 for(ii
= 0; ii
< strlist
->used
; ++ii
)
8430 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8433 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
8435 channelList_append(&chanserv_conf
.support_channels
, chan
);
8438 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
8441 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8444 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
8446 channelList_append(&chanserv_conf
.support_channels
, chan
);
8448 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
8449 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
8450 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
8451 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
8452 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
8453 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
8454 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
8455 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
8456 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
8457 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
8458 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
8459 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
8460 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
8461 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
8462 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
8463 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
8464 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
8465 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
8466 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
8467 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
8468 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
8469 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
8470 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
8471 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
8472 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
8474 NickChange(chanserv
, str
, 0);
8475 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
8476 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
8477 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
8478 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
8479 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
8480 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
8481 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
8482 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
8483 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
8484 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
8485 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
8486 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
8487 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
8488 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
8489 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
8490 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
8491 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
8492 god_timeout
= str
? ParseInterval(str
) : 60*15;
8493 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
8496 safestrncpy(mode_line
, str
, sizeof(mode_line
));
8497 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
8498 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
8499 && (change
->argc
< 2))
8501 chanserv_conf
.default_modes
= *change
;
8502 mod_chanmode_free(change
);
8504 str
= database_get_data(conf_node
, KEY_VALID_CHANNEL_REGEX
, RECDB_QSTRING
);
8505 if (chanserv_conf
.valid_channel_regex_set
)
8506 regfree(&chanserv_conf
.valid_channel_regex
);
8508 int err
= regcomp(&chanserv_conf
.valid_channel_regex
, str
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
);
8509 chanserv_conf
.valid_channel_regex_set
= !err
;
8510 if (err
) log_module(CS_LOG
, LOG_ERROR
, "Bad valid_channel_regex (error %d)", err
);
8512 chanserv_conf
.valid_channel_regex_set
= 0;
8514 free_string_list(chanserv_conf
.wheel
);
8515 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
8517 strlist
= string_list_copy(strlist
);
8520 static const char *list
[] = {
8521 "peer", "partall", "gline", /* "shun", */
8522 "nothing", "randjoin", "abusewhois", "kickall",
8523 "nickchange", "kill", "svsignore", "kickbanall",
8526 strlist
= alloc_string_list(ArrayLength(list
)-1);
8527 for(ii
=0; list
[ii
]; ii
++)
8528 string_list_append(strlist
, strdup(list
[ii
]));
8530 chanserv_conf
.wheel
= strlist
;
8532 free_string_list(chanserv_conf
.set_shows
);
8533 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
8535 strlist
= string_list_copy(strlist
);
8538 static const char *list
[] = {
8539 /* free form text */
8540 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8541 /* options based on user level */
8542 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8543 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
8544 /* multiple choice options */
8545 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
8546 /* binary options */
8547 "DynLimit", "NoDelete", "BanTimeout",
8552 strlist
= alloc_string_list(ArrayLength(list
)-1);
8553 for(ii
=0; list
[ii
]; ii
++)
8554 string_list_append(strlist
, strdup(list
[ii
]));
8556 chanserv_conf
.set_shows
= strlist
;
8557 /* We don't look things up now, in case the list refers to options
8558 * defined by modules initialized after this point. Just mark the
8559 * function list as invalid, so it will be initialized.
8561 set_shows_list
.used
= 0;
8563 free_string_list(chanserv_conf
.eightball
);
8564 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
8567 strlist
= string_list_copy(strlist
);
8571 strlist
= alloc_string_list(4);
8572 string_list_append(strlist
, strdup("Yes."));
8573 string_list_append(strlist
, strdup("No."));
8574 string_list_append(strlist
, strdup("Maybe so."));
8576 chanserv_conf
.eightball
= strlist
;
8578 free_string_list(chanserv_conf
.old_ban_names
);
8579 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
8581 strlist
= string_list_copy(strlist
);
8583 strlist
= alloc_string_list(2);
8584 chanserv_conf
.old_ban_names
= strlist
;
8585 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
8586 off_channel
= str
? atoi(str
) : 0;
8590 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
8593 struct note_type
*ntype
;
8596 if(!(obj
= GET_RECORD_OBJECT(rd
)))
8598 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
8601 if(!(ntype
= chanserv_create_note_type(key
)))
8603 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
8607 /* Figure out set access */
8608 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
8610 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8611 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
8613 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
8615 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
8616 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
8618 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
8620 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
8624 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
8625 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8626 ntype
->set_access
.min_opserv
= 0;
8629 /* Figure out visibility */
8630 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
8631 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8632 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
8633 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8634 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
8635 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
8636 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
8637 ntype
->visible_type
= NOTE_VIS_ALL
;
8639 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8641 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
8642 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
8646 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8648 struct handle_info
*handle
;
8649 struct userData
*uData
;
8650 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
8652 unsigned short access
, lastaccess
= 0;
8654 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8656 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
8660 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
8661 if(access
> UL_OWNER
)
8663 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
8667 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
8668 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
8669 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
8670 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
8671 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8672 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
8673 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
8674 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
8675 lastaccess
= lstacc
? atoi(lstacc
) : 0;
8677 handle
= get_handle_info(key
);
8680 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
8684 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
, 0);
8685 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
8686 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
8688 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
8689 if (uData
->accessexpiry
> 0)
8690 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
8692 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
8693 if (uData
->clvlexpiry
> 0)
8694 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
8696 uData
->lastaccess
= lastaccess
;
8698 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
8700 if(uData
->expires
> now
)
8701 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
8703 uData
->flags
&= ~USER_SUSPENDED
;
8706 /* Upgrade: set autoop to the inverse of noautoop */
8707 if(chanserv_read_version
< 2)
8709 /* if noautoop is true, set autoop false, and vice versa */
8710 if(uData
->flags
& USER_NOAUTO_OP
)
8711 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
8713 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
8714 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
);
8720 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8722 struct banData
*bData
;
8723 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
8724 time_t set_time
, triggered_time
, expires_time
;
8726 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8728 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8732 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8733 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8734 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8735 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8736 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8737 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8738 if (!reason
|| !owner
)
8741 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8742 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8744 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8746 expires_time
= set_time
+ atoi(s_duration
);
8750 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8753 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8756 static struct suspended
*
8757 chanserv_read_suspended(dict_t obj
)
8759 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8763 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8764 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8765 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8766 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8767 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8768 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8769 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8770 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8771 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8772 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8776 static struct giveownership
*
8777 chanserv_read_giveownership(dict_t obj
)
8779 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8783 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8784 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8786 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8788 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8789 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8791 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8792 giveownership
->reason
= str
? strdup(str
) : NULL
;
8793 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8794 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8796 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8797 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8798 return giveownership
;
8802 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8804 struct suspended
*suspended
;
8805 struct giveownership
*giveownership
;
8806 struct mod_chanmode
*modes
;
8807 struct chanNode
*cNode
;
8808 struct chanData
*cData
;
8809 struct dict
*channel
, *obj
;
8810 char *str
, *argv
[10];
8814 channel
= hir
->d
.object
;
8816 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8819 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8822 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8825 cData
= register_channel(cNode
, str
);
8828 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8832 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8834 enum levelOption lvlOpt
;
8835 enum charOption chOpt
;
8837 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8838 cData
->flags
= atoi(str
);
8840 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8842 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8844 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8845 else if(levelOptions
[lvlOpt
].old_flag
)
8847 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8848 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8850 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8854 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8856 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8858 cData
->chOpts
[chOpt
] = str
[0];
8861 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8863 enum levelOption lvlOpt
;
8864 enum charOption chOpt
;
8867 cData
->flags
= base64toint(str
, 5);
8868 count
= strlen(str
+= 5);
8869 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8872 if(levelOptions
[lvlOpt
].old_flag
)
8874 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8875 lvl
= levelOptions
[lvlOpt
].flag_value
;
8877 lvl
= levelOptions
[lvlOpt
].default_value
;
8879 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8881 case 'c': lvl
= UL_COOWNER
; break;
8882 case 'm': lvl
= UL_MANAGER
; break;
8883 case 'n': lvl
= UL_OWNER
+1; break;
8884 case 'o': lvl
= UL_OP
; break;
8885 case 'p': lvl
= UL_PEON
; break;
8886 case 'h': lvl
= UL_HALFOP
; break;
8887 case 'w': lvl
= UL_OWNER
; break;
8888 default: lvl
= 0; break;
8890 cData
->lvlOpts
[lvlOpt
] = lvl
;
8892 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8893 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8896 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8898 suspended
= chanserv_read_suspended(obj
);
8899 cData
->suspended
= suspended
;
8900 suspended
->cData
= cData
;
8901 /* We could use suspended->expires and suspended->revoked to
8902 * set the CHANNEL_SUSPENDED flag, but we don't. */
8904 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8906 suspended
= calloc(1, sizeof(*suspended
));
8907 suspended
->issued
= 0;
8908 suspended
->revoked
= 0;
8909 suspended
->suspender
= strdup(str
);
8910 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8911 suspended
->expires
= str
? atoi(str
) : 0;
8912 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8913 suspended
->reason
= strdup(str
? str
: "No reason");
8914 suspended
->previous
= NULL
;
8915 cData
->suspended
= suspended
;
8916 suspended
->cData
= cData
;
8920 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8921 suspended
= NULL
; /* to squelch a warning */
8924 if(IsSuspended(cData
)) {
8925 if(suspended
->expires
> now
)
8926 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8927 else if(suspended
->expires
)
8928 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8931 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8933 giveownership
= chanserv_read_giveownership(obj
);
8934 cData
->giveownership
= giveownership
;
8937 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8938 struct mod_chanmode change
;
8939 mod_chanmode_init(&change
);
8941 change
.args
[0].mode
= MODE_CHANOP
;
8942 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8943 mod_chanmode_announce(chanserv
, cNode
, &change
);
8946 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8947 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8948 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8949 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8950 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8951 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8952 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8953 cData
->max
= str
? atoi(str
) : 0;
8954 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8955 cData
->greeting
= str
? strdup(str
) : NULL
;
8956 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8957 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8958 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8959 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8960 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8961 cData
->topic
= str
? strdup(str
) : NULL
;
8963 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
8964 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
8966 if(!IsSuspended(cData
)
8967 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8968 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8969 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8970 cData
->modes
= *modes
;
8972 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8973 if(cData
->modes
.argc
> 1)
8974 cData
->modes
.argc
= 1;
8975 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8976 mod_chanmode_free(modes
);
8979 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8980 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8981 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8983 if(!cData
->users
&& !IsProtected(cData
))
8985 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8986 unregister_channel(cData
, "has empty user list.");
8990 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8991 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8992 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8994 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8995 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8997 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8998 struct record_data
*rd
= iter_data(it
);
8999 const char *note
, *setter
;
9001 if(rd
->type
!= RECDB_OBJECT
)
9003 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9007 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9009 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9011 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9015 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9016 if(!setter
) setter
= "<unknown>";
9017 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9025 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9027 const char *setter
, *reason
, *str
;
9028 struct do_not_register
*dnr
;
9030 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9033 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9036 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9039 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9042 dnr
= chanserv_add_dnr(key
, setter
, reason
);
9045 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9047 dnr
->set
= atoi(str
);
9053 chanserv_version_read(struct dict
*section
)
9057 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9059 chanserv_read_version
= atoi(str
);
9060 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9064 chanserv_saxdb_read(struct dict
*database
)
9066 struct dict
*section
;
9069 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9070 chanserv_version_read(section
);
9072 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9073 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9074 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9076 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9077 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9078 chanserv_channel_read(iter_key(it
), iter_data(it
));
9080 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9081 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9082 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9088 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9090 int high_present
= 0;
9091 saxdb_start_record(ctx
, KEY_USERS
, 1);
9092 for(; uData
; uData
= uData
->next
)
9094 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9096 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9097 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9098 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9099 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9100 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9101 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9103 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9105 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9107 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9108 saxdb_end_record(ctx
);
9110 saxdb_end_record(ctx
);
9111 return high_present
;
9115 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9119 saxdb_start_record(ctx
, KEY_BANS
, 1);
9120 for(; bData
; bData
= bData
->next
)
9122 saxdb_start_record(ctx
, bData
->mask
, 0);
9123 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9124 if(bData
->triggered
)
9125 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9127 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9129 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9131 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9132 saxdb_end_record(ctx
);
9134 saxdb_end_record(ctx
);
9138 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9140 saxdb_start_record(ctx
, name
, 0);
9141 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9142 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9144 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9146 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9148 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9150 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9151 saxdb_end_record(ctx
);
9155 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9157 saxdb_start_record(ctx
, name
, 0);
9158 if(giveownership
->staff_issuer
)
9159 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9160 if(giveownership
->old_owner
)
9161 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9162 if(giveownership
->target
)
9163 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9164 if(giveownership
->target_access
)
9165 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9166 if(giveownership
->reason
)
9167 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9168 if(giveownership
->issued
)
9169 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9170 if(giveownership
->previous
)
9171 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9172 saxdb_end_record(ctx
);
9176 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9180 enum levelOption lvlOpt
;
9181 enum charOption chOpt
;
9183 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9185 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9186 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9188 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9189 if(channel
->registrar
)
9190 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9191 if(channel
->greeting
)
9192 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9193 if(channel
->user_greeting
)
9194 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9195 if(channel
->topic_mask
)
9196 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9197 if(channel
->suspended
)
9198 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9199 if(channel
->giveownership
)
9200 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9202 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9203 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9204 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9205 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9206 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9208 buf
[0] = channel
->chOpts
[chOpt
];
9210 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9212 saxdb_end_record(ctx
);
9214 if (channel
->maxsetinfo
)
9215 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9217 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9219 mod_chanmode_format(&channel
->modes
, buf
);
9220 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9223 high_present
= chanserv_write_users(ctx
, channel
->users
);
9224 chanserv_write_bans(ctx
, channel
->bans
);
9226 if(dict_size(channel
->notes
))
9230 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9231 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9233 struct note
*note
= iter_data(it
);
9234 saxdb_start_record(ctx
, iter_key(it
), 0);
9235 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9236 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9237 saxdb_end_record(ctx
);
9239 saxdb_end_record(ctx
);
9242 if(channel
->ownerTransfer
)
9243 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9244 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9245 saxdb_end_record(ctx
);
9249 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9253 saxdb_start_record(ctx
, ntype
->name
, 0);
9254 switch(ntype
->set_access_type
)
9256 case NOTE_SET_CHANNEL_ACCESS
:
9257 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9259 case NOTE_SET_CHANNEL_SETTER
:
9260 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9262 case NOTE_SET_PRIVILEGED
: default:
9263 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9266 switch(ntype
->visible_type
)
9268 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9269 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9270 case NOTE_VIS_PRIVILEGED
:
9271 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9273 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9274 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9275 saxdb_end_record(ctx
);
9279 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9281 struct do_not_register
*dnr
;
9284 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
9286 dnr
= iter_data(it
);
9287 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9289 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9290 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9291 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9292 saxdb_end_record(ctx
);
9297 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9300 struct chanData
*channel
;
9302 /* Version Control*/
9303 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9304 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9305 saxdb_end_record(ctx
);
9308 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9309 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9310 chanserv_write_note_type(ctx
, iter_data(it
));
9311 saxdb_end_record(ctx
);
9314 saxdb_start_record(ctx
, KEY_DNR
, 1);
9315 write_dnrs_helper(ctx
, handle_dnrs
);
9316 write_dnrs_helper(ctx
, plain_dnrs
);
9317 write_dnrs_helper(ctx
, mask_dnrs
);
9318 saxdb_end_record(ctx
);
9321 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9322 for(channel
= channelList
; channel
; channel
= channel
->next
)
9323 chanserv_write_channel(ctx
, channel
);
9324 saxdb_end_record(ctx
);
9330 chanserv_db_cleanup(void) {
9332 unreg_part_func(handle_part
);
9334 unregister_channel(channelList
, "terminating.");
9335 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9336 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9337 free(chanserv_conf
.support_channels
.list
);
9338 dict_delete(handle_dnrs
);
9339 dict_delete(plain_dnrs
);
9340 dict_delete(mask_dnrs
);
9341 dict_delete(note_types
);
9342 free_string_list(chanserv_conf
.eightball
);
9343 free_string_list(chanserv_conf
.old_ban_names
);
9344 free_string_list(chanserv_conf
.wheel
);
9345 free_string_list(chanserv_conf
.set_shows
);
9346 free(set_shows_list
.list
);
9347 free(uset_shows_list
.list
);
9350 struct userData
*helper
= helperList
;
9351 helperList
= helperList
->next
;
9356 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
9357 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9358 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9361 init_chanserv(const char *nick
)
9363 struct chanNode
*chan
;
9366 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9367 conf_register_reload(chanserv_conf_read
);
9369 reg_server_link_func(handle_server_link
);
9371 reg_new_channel_func(handle_new_channel
);
9372 reg_join_func(handle_join
);
9373 reg_part_func(handle_part
);
9374 reg_kick_func(handle_kick
);
9375 reg_topic_func(handle_topic
);
9376 reg_mode_change_func(handle_mode
);
9377 reg_nick_change_func(handle_nick_change
);
9379 reg_auth_func(handle_auth
);
9380 reg_handle_rename_func(handle_rename
);
9381 reg_unreg_func(handle_unreg
);
9383 handle_dnrs
= dict_new();
9384 dict_set_free_data(handle_dnrs
, free
);
9385 plain_dnrs
= dict_new();
9386 dict_set_free_data(plain_dnrs
, free
);
9387 mask_dnrs
= dict_new();
9388 dict_set_free_data(mask_dnrs
, free
);
9390 reg_svccmd_unbind_func(handle_svccmd_unbind
);
9391 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
9392 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+channel", NULL
);
9393 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9394 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
9395 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
9396 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9397 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9398 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
9399 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
9401 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9403 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
9404 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
9406 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9407 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9408 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9409 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9410 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9412 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
9413 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
9414 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
9415 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9416 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9417 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9418 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9420 DEFINE_COMMAND(levels
, 1, 0, NULL
);
9422 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9423 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
9424 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9425 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
9427 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9428 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
9429 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9430 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9431 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
9432 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
9433 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9434 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9435 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9436 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9438 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9439 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9440 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9441 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
9442 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
9443 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
9444 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
9445 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
9446 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
9447 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
9448 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
9449 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
9450 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9451 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9453 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9454 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9455 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9456 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9458 /* if you change dellamer access, see also places
9459 * like unbanme which have manager hardcoded. */
9460 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9461 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
9463 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
9465 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
9467 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9468 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9469 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9470 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9471 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9472 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9473 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9474 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9475 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9476 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9477 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9478 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9480 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
9481 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
9483 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
9484 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
9485 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
9486 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
9488 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9489 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9490 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
9491 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
9492 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
9494 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9495 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9496 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9497 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9498 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9499 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9500 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9501 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9502 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9503 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9504 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
9506 /* Channel options */
9507 DEFINE_CHANNEL_OPTION(defaulttopic
);
9508 DEFINE_CHANNEL_OPTION(topicmask
);
9509 DEFINE_CHANNEL_OPTION(greeting
);
9510 DEFINE_CHANNEL_OPTION(usergreeting
);
9511 DEFINE_CHANNEL_OPTION(modes
);
9512 DEFINE_CHANNEL_OPTION(enfops
);
9513 DEFINE_CHANNEL_OPTION(enfhalfops
);
9514 DEFINE_CHANNEL_OPTION(automode
);
9515 DEFINE_CHANNEL_OPTION(protect
);
9516 DEFINE_CHANNEL_OPTION(enfmodes
);
9517 DEFINE_CHANNEL_OPTION(enftopic
);
9518 DEFINE_CHANNEL_OPTION(pubcmd
);
9519 DEFINE_CHANNEL_OPTION(userinfo
);
9520 DEFINE_CHANNEL_OPTION(dynlimit
);
9521 DEFINE_CHANNEL_OPTION(topicsnarf
);
9522 DEFINE_CHANNEL_OPTION(nodelete
);
9523 DEFINE_CHANNEL_OPTION(toys
);
9524 DEFINE_CHANNEL_OPTION(setters
);
9525 DEFINE_CHANNEL_OPTION(topicrefresh
);
9526 DEFINE_CHANNEL_OPTION(resync
);
9527 DEFINE_CHANNEL_OPTION(ctcpreaction
);
9528 DEFINE_CHANNEL_OPTION(bantimeout
);
9529 DEFINE_CHANNEL_OPTION(inviteme
);
9530 DEFINE_CHANNEL_OPTION(maxsetinfo
);
9532 DEFINE_CHANNEL_OPTION(offchannel
);
9533 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
9535 /* Alias set topic to set defaulttopic for compatibility. */
9536 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
9539 DEFINE_USER_OPTION(autoinvite
);
9540 DEFINE_USER_OPTION(autojoin
);
9541 DEFINE_USER_OPTION(info
);
9542 DEFINE_USER_OPTION(autoop
);
9544 /* Alias uset autovoice to uset autoop. */
9545 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
9547 note_types
= dict_new();
9548 dict_set_free_data(note_types
, chanserv_deref_note_type
);
9551 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
9552 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
9553 service_register(chanserv
)->trigger
= '!';
9554 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
9557 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
9559 if(chanserv_conf
.channel_expire_frequency
)
9560 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
9562 if(chanserv_conf
.ban_timeout_frequency
)
9563 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
9565 if(chanserv_conf
.refresh_period
)
9567 time_t next_refresh
;
9568 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
9569 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
9570 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
9573 if (autojoin_channels
&& chanserv
) {
9574 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
9575 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
9576 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
9580 reg_exit_func(chanserv_db_cleanup
);
9581 message_register_table(msgtab
);