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"
63 /* ChanServ database */
64 #define KEY_VERSION_CONTROL "version_control"
65 #define KEY_CHANNELS "channels"
66 #define KEY_NOTE_TYPES "note_types"
68 /* version control paramiter */
69 #define KEY_VERSION_NUMBER "version_number"
71 /* Note type parameters */
72 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
73 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
74 #define KEY_NOTE_SETTER_ACCESS "setter_access"
75 #define KEY_NOTE_VISIBILITY "visibility"
76 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
77 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
78 #define KEY_NOTE_VIS_ALL "all"
79 #define KEY_NOTE_MAX_LENGTH "max_length"
80 #define KEY_NOTE_SETTER "setter"
81 #define KEY_NOTE_NOTE "note"
83 /* Do-not-register channels */
85 #define KEY_DNR_SET "set"
86 #define KEY_DNR_SETTER "setter"
87 #define KEY_DNR_REASON "reason"
90 #define KEY_REGISTERED "registered"
91 #define KEY_REGISTRAR "registrar"
92 #define KEY_SUSPENDED "suspended"
93 #define KEY_PREVIOUS "previous"
94 #define KEY_SUSPENDER "suspender"
95 #define KEY_ISSUED "issued"
96 #define KEY_REVOKED "revoked"
97 #define KEY_SUSPEND_EXPIRES "suspend_expires"
98 #define KEY_SUSPEND_REASON "suspend_reason"
99 #define KEY_GIVEOWNERSHIP "giveownership"
100 #define KEY_STAFF_ISSUER "staff_issuer"
101 #define KEY_OLD_OWNER "old_owner"
102 #define KEY_TARGET "target"
103 #define KEY_TARGET_ACCESS "target_access"
104 #define KEY_VISITED "visited"
105 #define KEY_TOPIC "topic"
106 #define KEY_GREETING "greeting"
107 #define KEY_USER_GREETING "user_greeting"
108 #define KEY_MODES "modes"
109 #define KEY_FLAGS "flags"
110 #define KEY_OPTIONS "options"
111 #define KEY_USERS "users"
112 #define KEY_BANS "bans" /* for lamers */
113 #define KEY_MAX "max"
114 #define KEY_NOTES "notes"
115 #define KEY_TOPIC_MASK "topic_mask"
116 #define KEY_OWNER_TRANSFER "owner_transfer"
117 #define KEY_MAXSETINFO "maxsetinfo"
120 #define KEY_LEVEL "level"
121 #define KEY_INFO "info"
122 #define KEY_SEEN "seen"
123 #define KEY_ACCESSEXPIRY "accessexpiry"
124 #define KEY_CLVLEXPIRY "clvlexpiry"
125 #define KEY_LASTLEVEL "lastlevel"
128 #define KEY_OWNER "owner"
129 #define KEY_REASON "reason"
130 #define KEY_SET "set"
131 #define KEY_DURATION "duration"
132 #define KEY_EXPIRES "expires"
133 #define KEY_TRIGGERED "triggered"
135 #define KEY_GOD_TIMEOUT "god_timeout"
137 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
138 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
140 /* Administrative messages */
141 static const struct message_entry msgtab
[] = {
142 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
144 /* Channel registration */
145 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
146 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
147 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
148 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
149 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
150 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
151 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
152 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
154 /* Do-not-register channels */
155 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
156 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
157 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
158 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
159 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
160 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
161 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
162 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
163 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
164 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
165 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
167 /* Channel unregistration */
168 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
169 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
170 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
171 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
174 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
175 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
177 /* Channel merging */
178 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
179 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
180 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
181 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
182 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
184 /* Handle unregistration */
185 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
188 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
189 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
190 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
191 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
192 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
193 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
194 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
195 { "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." },
196 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
197 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
198 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
199 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
200 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
201 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
202 { "CSMSG_NOT_IN_CHANNEL", "I am not in %s." },
204 /* Removing yourself from a channel. */
205 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
206 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
207 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
209 /* User management */
210 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
211 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
212 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
213 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
214 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
215 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
216 { "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." },
217 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
218 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
219 { "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." },
220 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
221 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
222 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
223 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
224 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
225 { "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" },
226 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
228 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
229 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
230 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
231 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
232 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
233 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
234 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
235 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
238 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
239 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
240 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
241 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
242 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
243 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
244 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
245 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
246 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
247 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
248 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
249 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
250 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
251 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
252 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
253 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
254 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
256 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
258 /* Channel management */
259 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
260 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
261 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
263 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
264 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
265 { "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" },
266 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
267 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
268 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
269 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
271 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
272 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
273 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
274 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
275 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
276 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
277 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
278 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
279 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
280 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
281 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
282 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
283 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
284 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
285 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
286 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
287 { "CSMSG_SET_MODES", "$bModes $b %s" },
288 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
289 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
290 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
291 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
292 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
293 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
294 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
295 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
296 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
297 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
298 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
299 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
300 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
301 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
302 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
303 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
304 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
305 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
306 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
307 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
309 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
310 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
311 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
312 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
313 { "CSMSG_USET_INFO", "$bInfo $b %s" },
315 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
316 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
317 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
318 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
319 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
320 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
321 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
322 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
323 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
324 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
325 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
327 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
328 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
329 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
330 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
331 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
332 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
333 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
335 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
336 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
337 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
338 { "CSMSG_PROTECT_NONE", "No users will be protected." },
339 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
340 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
341 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
343 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
344 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
345 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
346 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
347 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
349 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
350 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
351 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
352 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
353 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
355 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
356 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
357 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
358 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
359 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
361 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
362 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
363 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
364 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
365 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
366 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
368 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
369 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
370 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
371 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
372 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
373 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
374 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
375 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
376 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
378 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
379 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
380 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
382 /* Channel userlist */
383 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
384 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
385 /* uncomment if needed to adujust styles (and change code below)
386 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
387 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
388 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
389 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
390 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
391 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
393 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
394 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
395 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
397 /* Channel note list */
398 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
399 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
400 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
401 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
402 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
403 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
404 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
405 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
406 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
407 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
408 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
409 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
410 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
411 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
412 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
414 /* Channel [un]suspension */
415 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
416 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
417 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
418 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
419 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
420 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
421 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
423 /* Access information */
424 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
425 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
426 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
427 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
428 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
429 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
430 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
431 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
432 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
433 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
434 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
436 /* Seen information */
437 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
438 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
439 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
440 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
442 /* Names information */
443 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
444 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
446 /* Channel information */
447 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
448 { "CSMSG_BAR", "----------------------------------------"},
449 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
450 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
451 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
452 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
453 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
454 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
455 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
456 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
457 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
458 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
459 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
460 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
461 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
462 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
463 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
464 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
465 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
466 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
467 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
468 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
469 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
470 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
471 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
472 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
473 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
474 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
476 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
477 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
478 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
479 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
480 { "CSMSG_PEEK_OPS", "$bOps:$b" },
481 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
482 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
484 /* Network information */
485 { "CSMSG_NETWORK_INFO", "Network Information:" },
486 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
487 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
488 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
489 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
490 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
491 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
492 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
493 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
496 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
497 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
498 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
500 /* Channel searches */
501 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
502 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
503 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
504 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
506 /* Channel configuration */
507 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
508 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
509 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
510 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
513 { "CSMSG_USER_OPTIONS", "User Options:" },
514 // { "CSMSG_USER_PROTECTED", "That user is protected." },
517 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
518 { "CSMSG_PING_RESPONSE", "Pong!" },
519 { "CSMSG_WUT_RESPONSE", "wut" },
520 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
521 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
522 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
523 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
524 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
525 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
526 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
527 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
528 { "CSMSG_ROULETTE_NEW", "Please type .roulette to start a new round" } ,
529 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
530 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
531 { "CSMSG_ROULETTE_CLICK", "Click" } ,
533 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
534 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
535 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
537 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
538 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
539 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
540 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
541 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
542 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
543 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
544 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
545 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
546 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
547 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
548 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
549 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
550 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
553 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
554 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
555 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
556 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
560 /* eject_user and unban_user flags */
561 #define ACTION_KICK 0x0001
562 #define ACTION_BAN 0x0002
563 #define ACTION_ADD_LAMER 0x0004
564 #define ACTION_ADD_TIMED_LAMER 0x0008
565 #define ACTION_UNBAN 0x0010
566 #define ACTION_DEL_LAMER 0x0020
568 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
569 #define MODELEN 40 + KEYLEN
573 #define CSFUNC_ARGS user, channel, argc, argv, cmd
575 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
576 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
577 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
578 reply("MSG_MISSING_PARAMS", argv[0]); \
582 DECLARE_LIST(dnrList
, struct do_not_register
*);
583 DEFINE_LIST(dnrList
, struct do_not_register
*);
585 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
587 struct userNode
*chanserv
;
590 extern struct string_list
*autojoin_channels
;
591 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
592 static struct log_type
*CS_LOG
;
593 struct adduserPending
* adduser_pendings
= NULL
;
594 unsigned int adduser_pendings_count
= 0;
595 unsigned long god_timeout
;
599 struct channelList support_channels
;
600 struct mod_chanmode default_modes
;
602 unsigned long db_backup_frequency
;
603 unsigned long channel_expire_frequency
;
604 unsigned long ban_timeout_frequency
;
607 unsigned int adjust_delay
;
608 long channel_expire_delay
;
609 unsigned int nodelete_level
;
611 unsigned int adjust_threshold
;
612 int join_flood_threshold
;
614 unsigned int greeting_length
;
615 unsigned int refresh_period
;
616 unsigned int giveownership_period
;
618 unsigned int max_owned
;
619 unsigned int max_chan_users
;
620 unsigned int max_chan_bans
; /* lamers */
621 unsigned int max_userinfo_length
;
623 struct string_list
*set_shows
;
624 struct string_list
*eightball
;
625 struct string_list
*old_ban_names
;
626 struct string_list
*wheel
;
628 const char *ctcp_short_ban_duration
;
629 const char *ctcp_long_ban_duration
;
631 const char *irc_operator_epithet
;
632 const char *network_helper_epithet
;
633 const char *support_helper_epithet
;
638 struct userNode
*user
;
639 struct userNode
*bot
;
640 struct chanNode
*channel
;
642 unsigned short lowest
;
643 unsigned short highest
;
644 struct userData
**users
;
645 struct helpfile_table table
;
648 enum note_access_type
650 NOTE_SET_CHANNEL_ACCESS
,
651 NOTE_SET_CHANNEL_SETTER
,
655 enum note_visible_type
658 NOTE_VIS_CHANNEL_USERS
,
662 struct io_fd
*socket_io_fd
;
663 extern struct cManagerNode cManager
;
667 enum note_access_type set_access_type
;
669 unsigned int min_opserv
;
670 unsigned short min_ulevel
;
672 enum note_visible_type visible_type
;
673 unsigned int max_length
;
680 struct note_type
*type
;
681 char setter
[NICKSERV_HANDLE_LEN
+1];
685 static unsigned int registered_channels
;
686 static unsigned int banCount
;
688 static const struct {
691 unsigned short level
;
693 } accessLevels
[] = { /* MUST be orderd less to most! */
694 { "pal", "Pal", UL_PEON
, '+' },
695 { "peon", "Peon", UL_PEON
, '+' },
696 { "halfop", "HalfOp", UL_HALFOP
, '%' },
697 { "op", "Op", UL_OP
, '@' },
698 { "manager", "Manager", UL_MANAGER
, '%' },
699 { "coowner", "Coowner", UL_COOWNER
, '*' },
700 { "owner", "Owner", UL_OWNER
, '!' },
701 { "helper", "BUG:", UL_HELPER
, 'X' }
704 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
705 static const struct {
708 unsigned short default_value
;
709 unsigned int old_idx
;
710 unsigned int old_flag
;
711 unsigned short flag_value
;
713 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
714 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
715 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
716 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
717 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
718 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
719 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
720 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
721 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
724 struct charOptionValues
{
727 } automodeValues
[] = {
728 { 'n', "CSMSG_AUTOMODE_NONE" },
729 { 'y', "CSMSG_AUTOMODE_NORMAL" },
730 { 'v', "CSMSG_AUTOMODE_VOICE" },
731 { 'h', "CSMSG_AUTOMODE_HOP" },
732 { 'o', "CSMSG_AUTOMODE_OP" },
733 { 'm', "CSMSG_AUTOMODE_MUTE" },
734 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
735 }, protectValues
[] = {
736 { 'a', "CSMSG_PROTECT_ALL" },
737 { 'e', "CSMSG_PROTECT_EQUAL" },
738 { 'l', "CSMSG_PROTECT_LOWER" },
739 { 'n', "CSMSG_PROTECT_NONE" }
741 { 'd', "CSMSG_TOYS_DISABLED" },
742 { 'n', "CSMSG_TOYS_PRIVATE" },
743 { 'p', "CSMSG_TOYS_PUBLIC" }
744 }, topicRefreshValues
[] = {
745 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
746 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
747 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
748 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
749 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
750 }, ctcpReactionValues
[] = {
751 { 'n', "CSMSG_CTCPREACTION_NONE" },
752 { 'k', "CSMSG_CTCPREACTION_KICK" },
753 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
754 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
755 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
756 }, banTimeoutValues
[] = {
757 { '0', "CSMSG_BANTIMEOUT_NONE" },
758 { '1', "CSMSG_BANTIMEOUT_10M" },
759 { '2', "CSMSG_BANTIMEOUT_2H" },
760 { '3', "CSMSG_BANTIMEOUT_4H" },
761 { '4', "CSMSG_BANTIMEOUT_1D" },
762 { '5', "CSMSG_BANTIMEOUT_1W" }
765 { 'n', "CSMSG_RESYNC_NEVER" },
766 { '1', "CSMSG_RESYNC_3_HOURS" },
767 { '2', "CSMSG_RESYNC_6_HOURS" },
768 { '3', "CSMSG_RESYNC_12_HOURS" },
769 { '4', "CSMSG_RESYNC_24_HOURS" }
772 static const struct {
776 unsigned int old_idx
;
778 struct charOptionValues
*values
;
780 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
781 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
782 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
783 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
784 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
785 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
786 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
789 struct userData
*helperList
;
790 struct chanData
*channelList
;
791 static struct module *chanserv_module
;
792 static unsigned int userCount
;
793 unsigned int chanserv_read_version
= 0; /* db version control */
795 #define CHANSERV_DB_VERSION 2
797 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
798 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
800 void sputsock(const char *text
, ...) PRINTF_LIKE(1, 2);
803 sputsock(const char *text
, ...)
809 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
811 va_start(arg_list
, text
);
812 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
814 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
816 log_replay(MAIN_LOG
, true, buffer
);
817 buffer
[pos
++] = '\n';
819 ioset_write(socket_io_fd
, buffer
, pos
);
823 user_level_from_name(const char *name
, unsigned short clamp_level
)
825 unsigned int level
= 0, ii
;
827 level
= strtoul(name
, NULL
, 10);
828 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
829 if(!irccasecmp(name
, accessLevels
[ii
].name
))
830 level
= accessLevels
[ii
].level
;
831 if(level
> clamp_level
)
837 user_level_name_from_level(int level
)
845 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
846 if(level
>= accessLevels
[ii
].level
)
847 highest
= accessLevels
[ii
].title
;
853 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
856 *minl
= strtoul(arg
, &sep
, 10);
864 *maxl
= strtoul(sep
+1, &sep
, 10);
872 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
874 struct userData
*uData
, **head
;
876 if(!channel
|| !handle
)
879 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
880 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
882 for(uData
= helperList
;
883 uData
&& uData
->handle
!= handle
;
884 uData
= uData
->next
);
888 uData
= calloc(1, sizeof(struct userData
));
889 uData
->handle
= handle
;
891 uData
->access
= UL_HELPER
;
897 uData
->next
= helperList
;
899 helperList
->prev
= uData
;
907 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
908 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
911 head
= &(channel
->users
);
914 if(uData
&& (uData
!= *head
))
916 /* Shuffle the user to the head of whatever list he was in. */
918 uData
->next
->prev
= uData
->prev
;
920 uData
->prev
->next
= uData
->next
;
926 (**head
).prev
= uData
;
933 /* Returns non-zero if user has at least the minimum access.
934 * exempt_owner is set when handling !set, so the owner can set things
937 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
939 struct userData
*uData
;
940 struct chanData
*cData
= channel
->channel_info
;
941 unsigned short minimum
= cData
->lvlOpts
[opt
];
944 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
947 if(minimum
<= uData
->access
)
949 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
954 /* Scan for other users authenticated to the same handle
955 still in the channel. If so, keep them listed as present.
957 user is optional, if not null, it skips checking that userNode
958 (for the handle_part function) */
960 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
964 if(IsSuspended(uData
->channel
)
965 || IsUserSuspended(uData
)
966 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
978 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
980 unsigned int eflags
, argc
;
982 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
984 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
985 if(!channel
->channel_info
986 || IsSuspended(channel
->channel_info
)
988 || !ircncasecmp(text
, "ACTION ", 7))
990 /* We dont punish people we know -Rubin
991 * * Figure out the minimum level needed to CTCP the channel *
993 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
996 /* If they are a user of the channel, they are exempt */
997 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
999 /* We need to enforce against them; do so. */
1002 argv
[1] = user
->nick
;
1004 if(GetUserMode(channel
, user
))
1005 eflags
|= ACTION_KICK
;
1006 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
1007 default: case 'n': return;
1009 eflags
|= ACTION_KICK
;
1012 eflags
|= ACTION_BAN
;
1015 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1016 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
1019 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
1020 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
1023 argv
[argc
++] = bad_ctcp_reason
;
1024 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
1028 chanserv_create_note_type(const char *name
)
1030 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
1031 strcpy(ntype
->name
, name
);
1033 dict_insert(note_types
, ntype
->name
, ntype
);
1038 chanserv_deref_note_type(void *data
)
1040 struct note_type
*ntype
= data
;
1042 if(--ntype
->refs
> 0)
1048 chanserv_flush_note_type(struct note_type
*ntype
)
1050 struct chanData
*cData
;
1051 for(cData
= channelList
; cData
; cData
= cData
->next
)
1052 dict_remove(cData
->notes
, ntype
->name
);
1056 chanserv_truncate_notes(struct note_type
*ntype
)
1058 struct chanData
*cData
;
1060 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1062 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1063 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1066 if(strlen(note
->note
) <= ntype
->max_length
)
1068 dict_remove2(cData
->notes
, ntype
->name
, 1);
1069 note
= realloc(note
, size
);
1070 note
->note
[ntype
->max_length
] = 0;
1071 dict_insert(cData
->notes
, ntype
->name
, note
);
1075 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1077 static struct note
*
1078 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1081 unsigned int len
= strlen(text
);
1083 if(len
> type
->max_length
) len
= type
->max_length
;
1084 note
= calloc(1, sizeof(*note
) + len
);
1086 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1087 memcpy(note
->note
, text
, len
);
1088 note
->note
[len
] = 0;
1089 dict_insert(channel
->notes
, type
->name
, note
);
1095 chanserv_free_note(void *data
)
1097 struct note
*note
= data
;
1099 chanserv_deref_note_type(note
->type
);
1100 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1104 static MODCMD_FUNC(cmd_createnote
) {
1105 struct note_type
*ntype
;
1106 unsigned int arg
= 1, existed
= 0, max_length
;
1108 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1111 ntype
= chanserv_create_note_type(argv
[arg
]);
1112 if(!irccasecmp(argv
[++arg
], "privileged"))
1115 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1116 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1118 else if(!irccasecmp(argv
[arg
], "channel"))
1120 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1123 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1126 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1127 ntype
->set_access
.min_ulevel
= ulvl
;
1129 else if(!irccasecmp(argv
[arg
], "setter"))
1131 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1135 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1139 if(!irccasecmp(argv
[++arg
], "privileged"))
1140 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1141 else if(!irccasecmp(argv
[arg
], "channel_users"))
1142 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1143 else if(!irccasecmp(argv
[arg
], "all"))
1144 ntype
->visible_type
= NOTE_VIS_ALL
;
1146 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1150 if((arg
+1) >= argc
) {
1151 reply("MSG_MISSING_PARAMS", argv
[0]);
1154 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1155 if(max_length
< 20 || max_length
> 450)
1157 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1160 if(existed
&& (max_length
< ntype
->max_length
))
1162 ntype
->max_length
= max_length
;
1163 chanserv_truncate_notes(ntype
);
1165 ntype
->max_length
= max_length
;
1168 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1170 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1175 dict_remove(note_types
, ntype
->name
);
1179 static MODCMD_FUNC(cmd_removenote
) {
1180 struct note_type
*ntype
;
1183 ntype
= dict_find(note_types
, argv
[1], NULL
);
1184 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1187 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1194 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1197 chanserv_flush_note_type(ntype
);
1199 dict_remove(note_types
, argv
[1]);
1200 reply("CSMSG_NOTE_DELETED", argv
[1]);
1205 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1209 if(orig
->modes_set
& change
->modes_clear
)
1211 if(orig
->modes_clear
& change
->modes_set
)
1213 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1214 && strcmp(orig
->new_key
, change
->new_key
))
1216 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1217 && (orig
->new_limit
!= change
->new_limit
))
1222 static char max_length_text
[MAXLEN
+1][16];
1224 static struct helpfile_expansion
1225 chanserv_expand_variable(const char *variable
)
1227 struct helpfile_expansion exp
;
1229 if(!irccasecmp(variable
, "notes"))
1232 exp
.type
= HF_TABLE
;
1233 exp
.value
.table
.length
= 1;
1234 exp
.value
.table
.width
= 3;
1235 exp
.value
.table
.flags
= 0;
1236 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1237 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1238 exp
.value
.table
.contents
[0][0] = "Note Type";
1239 exp
.value
.table
.contents
[0][1] = "Visibility";
1240 exp
.value
.table
.contents
[0][2] = "Max Length";
1241 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1243 struct note_type
*ntype
= iter_data(it
);
1246 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1247 row
= exp
.value
.table
.length
++;
1248 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1249 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1250 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1251 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1253 if(!max_length_text
[ntype
->max_length
][0])
1254 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1255 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1260 exp
.type
= HF_STRING
;
1261 exp
.value
.str
= NULL
;
1265 static struct chanData
*
1266 register_channel(struct chanNode
*cNode
, char *registrar
)
1268 struct chanData
*channel
;
1269 enum levelOption lvlOpt
;
1270 enum charOption chOpt
;
1272 channel
= calloc(1, sizeof(struct chanData
));
1274 channel
->notes
= dict_new();
1275 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1277 channel
->registrar
= strdup(registrar
);
1278 channel
->registered
= now
;
1279 channel
->visited
= now
;
1280 channel
->limitAdjusted
= now
;
1281 channel
->ownerTransfer
= now
;
1282 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1283 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1284 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1285 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1286 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1288 channel
->prev
= NULL
;
1289 channel
->next
= channelList
;
1292 channelList
->prev
= channel
;
1293 channelList
= channel
;
1294 registered_channels
++;
1296 channel
->channel
= cNode
;
1298 cNode
->channel_info
= channel
;
1303 static struct userData
*
1304 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
, time_t accessexpiry
)
1306 struct userData
*ud
;
1308 if(access
> UL_OWNER
)
1311 ud
= calloc(1, sizeof(*ud
));
1312 ud
->channel
= channel
;
1313 ud
->handle
= handle
;
1315 ud
->access
= access
;
1316 ud
->info
= info
? strdup(info
) : NULL
;
1317 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1322 ud
->next
= channel
->users
;
1324 channel
->users
->prev
= ud
;
1325 channel
->users
= ud
;
1327 channel
->userCount
++;
1331 ud
->u_next
= ud
->handle
->channels
;
1333 ud
->u_next
->u_prev
= ud
;
1334 ud
->handle
->channels
= ud
;
1336 ud
->flags
= USER_FLAGS_DEFAULT
;
1340 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1343 chanserv_expire_tempuser(void *data
)
1345 struct userData
*uData
= data
;
1349 handle
= strdup(uData
->handle
->handle
);
1350 if (uData
->accessexpiry
> 0) {
1351 if (uData
->present
) {
1352 struct userNode
*user
, *next_un
= NULL
;
1353 struct handle_info
*hi
;
1355 hi
= get_handle_info(handle
);
1356 for (user
= hi
->users
; user
; user
= next_un
) {
1357 struct mod_chanmode
*change
;
1358 struct modeNode
*mn
;
1359 unsigned int count
= 0;
1361 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1362 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1363 next_un
= user
->next_authed
;
1367 change
= mod_chanmode_alloc(2);
1368 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1369 change
->args
[count
++].u
.member
= mn
;
1372 change
->argc
= count
;
1373 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1375 mod_chanmode_free(change
);
1376 next_un
= user
->next_authed
;
1379 del_channel_user(uData
, 1);
1385 chanserv_expire_tempclvl(void *data
)
1387 struct userData
*uData
= data
;
1391 handle
= strdup(uData
->handle
->handle
);
1392 if (uData
->clvlexpiry
> 0) {
1393 int changemodes
= 0;
1394 unsigned int mode
= 0;
1396 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1398 mode
= MODE_REMOVE
| MODE_CHANOP
;
1399 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1401 mode
= MODE_REMOVE
| MODE_HALFOP
;
1405 if (uData
->present
) {
1406 struct userNode
*user
, *next_un
= NULL
;
1407 struct handle_info
*hi
;
1409 hi
= get_handle_info(handle
);
1410 for (user
= hi
->users
; user
; user
= next_un
) {
1411 struct mod_chanmode
*change
;
1412 struct modeNode
*mn
;
1413 unsigned int count
= 0;
1415 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1416 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1417 next_un
= user
->next_authed
;
1421 if (changemodes
== 0) {
1422 next_un
= user
->next_authed
;
1426 change
= mod_chanmode_alloc(2);
1427 change
->args
[count
].mode
= mode
;
1428 change
->args
[count
++].u
.member
= mn
;
1431 change
->argc
= count
;
1432 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1434 mod_chanmode_free(change
);
1435 next_un
= user
->next_authed
;
1439 uData
->access
= uData
->lastaccess
;
1440 uData
->lastaccess
= 0;
1441 uData
->clvlexpiry
= 0;
1447 del_channel_user(struct userData
*user
, int do_gc
)
1449 struct chanData
*channel
= user
->channel
;
1451 channel
->userCount
--;
1454 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1455 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1458 user
->prev
->next
= user
->next
;
1460 channel
->users
= user
->next
;
1462 user
->next
->prev
= user
->prev
;
1465 user
->u_prev
->u_next
= user
->u_next
;
1467 user
->handle
->channels
= user
->u_next
;
1469 user
->u_next
->u_prev
= user
->u_prev
;
1473 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1474 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1475 unregister_channel(channel
, "lost all users.");
1479 static struct adduserPending
*
1480 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1482 struct adduserPending
*ap
;
1483 ap
= calloc(1,sizeof(struct adduserPending
));
1484 ap
->channel
= channel
;
1487 ap
->created
= time(NULL
);
1489 /* ap->prev defaults to NULL already.. */
1490 ap
->next
= adduser_pendings
;
1491 if(adduser_pendings
)
1492 adduser_pendings
->prev
= ap
;
1493 adduser_pendings
= ap
;
1494 adduser_pendings_count
++;
1499 del_adduser_pending(struct adduserPending
*ap
)
1502 ap
->prev
->next
= ap
->next
;
1504 adduser_pendings
= ap
->next
;
1507 ap
->next
->prev
= ap
->prev
;
1511 static void expire_adduser_pending();
1513 /* find_adduser_pending(channel, user) will find an arbitrary record
1514 * from user, channel, or user and channel.
1515 * if user or channel are NULL, they will match any records.
1517 static struct adduserPending
*
1518 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1520 struct adduserPending
*ap
;
1522 expire_adduser_pending(); /* why not here.. */
1524 if(!channel
&& !user
) /* 2 nulls matches all */
1525 return(adduser_pendings
);
1526 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1528 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1535 /* Remove all pendings for a user or channel
1537 * called in nickserv.c DelUser() and proto-* unregister_channel()
1540 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1542 struct adduserPending
*ap
;
1544 /* So this is a bit wastefull, i hate dealing with linked lists.
1545 * if its a problem we'll rewrite it right */
1546 while((ap
= find_adduser_pending(channel
, user
))) {
1547 del_adduser_pending(ap
);
1551 /* Called from nickserv.c cmd_auth after someone auths */
1553 process_adduser_pending(struct userNode
*user
)
1555 struct adduserPending
*ap
;
1556 if(!user
->handle_info
)
1557 return; /* not associated with an account */
1558 while((ap
= find_adduser_pending(NULL
, user
)))
1560 struct userData
*actee
;
1561 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1563 /* Already on the userlist. do nothing*/
1567 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1568 scan_user_presence(actee
, NULL
);
1570 del_adduser_pending(ap
);
1575 expire_adduser_pending()
1577 struct adduserPending
*ap
, *ap_next
;
1578 ap
= adduser_pendings
;
1581 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1583 ap_next
= ap
->next
; /* save next */
1584 del_adduser_pending(ap
); /* free and relink */
1585 ap
= ap_next
; /* advance */
1592 static void expire_ban(void *data
);
1595 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1598 unsigned int ii
, l1
, l2
;
1603 bd
= malloc(sizeof(struct banData
));
1605 bd
->channel
= channel
;
1607 bd
->triggered
= triggered
;
1608 bd
->expires
= expires
;
1610 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1612 extern const char *hidden_host_suffix
;
1613 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1617 l2
= strlen(old_name
);
1620 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1622 new_mask
= alloca(MAXLEN
);
1623 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1626 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1628 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1629 bd
->reason
= strdup(reason
);
1632 timeq_add(expires
, expire_ban
, bd
);
1635 bd
->next
= channel
->bans
; /* lamers */
1637 channel
->bans
->prev
= bd
;
1639 channel
->banCount
++;
1646 del_channel_ban(struct banData
*ban
)
1648 ban
->channel
->banCount
--;
1652 ban
->prev
->next
= ban
->next
;
1654 ban
->channel
->bans
= ban
->next
;
1657 ban
->next
->prev
= ban
->prev
;
1660 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1669 expire_ban(void *data
) /* lamer.. */
1671 struct banData
*bd
= data
;
1672 if(!IsSuspended(bd
->channel
))
1674 struct banList bans
;
1675 struct mod_chanmode change
;
1677 bans
= bd
->channel
->channel
->banlist
;
1678 mod_chanmode_init(&change
);
1679 for(ii
=0; ii
<bans
.used
; ii
++)
1681 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1684 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1685 change
.args
[0].u
.hostmask
= bd
->mask
;
1686 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1692 del_channel_ban(bd
);
1695 static void chanserv_expire_suspension(void *data
);
1698 unregister_channel(struct chanData
*channel
, const char *reason
)
1700 struct mod_chanmode change
;
1701 char msgbuf
[MAXLEN
];
1703 /* After channel unregistration, the following must be cleaned
1705 - Channel information.
1707 - Channel bans. (lamers)
1708 - Channel suspension data.
1709 - adduser_pending data.
1710 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1716 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1720 mod_chanmode_init(&change
);
1721 change
.modes_clear
|= MODE_REGISTERED
;
1722 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1725 wipe_adduser_pending(channel
->channel
, NULL
);
1727 while(channel
->users
)
1728 del_channel_user(channel
->users
, 0);
1730 while(channel
->bans
)
1731 del_channel_ban(channel
->bans
);
1733 free(channel
->topic
);
1734 free(channel
->registrar
);
1735 free(channel
->greeting
);
1736 free(channel
->user_greeting
);
1737 free(channel
->topic_mask
);
1740 channel
->prev
->next
= channel
->next
;
1742 channelList
= channel
->next
;
1745 channel
->next
->prev
= channel
->prev
;
1747 if(channel
->suspended
)
1749 struct chanNode
*cNode
= channel
->channel
;
1750 struct suspended
*suspended
, *next_suspended
;
1752 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1754 next_suspended
= suspended
->previous
;
1755 free(suspended
->suspender
);
1756 free(suspended
->reason
);
1757 if(suspended
->expires
)
1758 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1763 cNode
->channel_info
= NULL
;
1765 channel
->channel
->channel_info
= NULL
;
1767 dict_delete(channel
->notes
);
1768 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1769 if(!IsSuspended(channel
))
1770 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1771 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1772 UnlockChannel(channel
->channel
);
1774 registered_channels
--;
1778 expire_channels(UNUSED_ARG(void *data
))
1780 struct chanData
*channel
, *next
;
1781 struct userData
*user
;
1782 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1784 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1785 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1787 for(channel
= channelList
; channel
; channel
= next
)
1789 next
= channel
->next
;
1791 /* See if the channel can be expired. */
1792 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1793 || IsProtected(channel
))
1796 /* Make sure there are no high-ranking users still in the channel. */
1797 for(user
=channel
->users
; user
; user
=user
->next
)
1798 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1803 /* Unregister the channel */
1804 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1805 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1806 unregister_channel(channel
, "registration expired.");
1809 if(chanserv_conf
.channel_expire_frequency
)
1810 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1814 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1816 char protect
= channel
->chOpts
[chProtect
];
1817 struct userData
*cs_victim
, *cs_aggressor
;
1819 /* If victim access level is greater than set invitelevel, don't let
1820 * us kick them, but don't consider it punishment if someone else does
1824 if(victim
== aggressor
)
1826 /* Don't protect if the victim isn't authenticated (because they
1827 can't be a channel user), unless we are to protect non-users
1830 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1832 /* If they have enough access to invite themselvs through a ban,
1833 * and its us kicking them, don't. -Rubin */
1834 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1840 if(protect
!= 'a' && !cs_victim
)
1843 /* Protect if the aggressor isn't a user because at this point,
1844 the aggressor can only be less than or equal to the victim. */
1846 /* Not protected from chanserv except above */
1847 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1848 if(aggressor
== chanserv
)
1851 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1855 /* If the aggressor was a user, then the victim can't be helped. */
1862 if(cs_victim
->access
> cs_aggressor
->access
)
1867 if(cs_victim
->access
>= cs_aggressor
->access
)
1876 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1878 struct chanData
*cData
= channel
->channel_info
;
1879 struct userData
*cs_victim
;
1881 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1882 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1883 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1886 reply("CSMSG_OPBY_LOCKED");
1888 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1896 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1898 struct chanData
*cData
= channel
->channel_info
;
1899 struct userData
*cs_victim
;
1901 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1902 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1903 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1905 reply("CSMSG_HOPBY_LOCKED");
1914 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1916 if(IsService(victim
))
1918 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1922 if(protect_user(victim
, user
, channel
->channel_info
, false))
1924 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1932 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1934 if(IsService(victim
))
1936 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1940 if(protect_user(victim
, user
, channel
->channel_info
, false))
1942 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1949 static struct do_not_register
*
1950 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1952 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1953 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1954 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1955 strcpy(dnr
->reason
, reason
);
1957 if(dnr
->chan_name
[0] == '*')
1958 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1959 else if(strpbrk(dnr
->chan_name
, "*?"))
1960 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1962 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1966 static struct dnrList
1967 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1969 struct dnrList list
;
1971 struct do_not_register
*dnr
;
1973 dnrList_init(&list
);
1974 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1975 dnrList_append(&list
, dnr
);
1976 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1977 dnrList_append(&list
, dnr
);
1979 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1980 if(match_ircglob(chan_name
, iter_key(it
)))
1981 dnrList_append(&list
, iter_data(it
));
1986 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1988 struct dnrList list
;
1989 struct do_not_register
*dnr
;
1991 char buf
[INTERVALLEN
];
1993 list
= chanserv_find_dnrs(chan_name
, handle
);
1994 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1996 dnr
= list
.list
[ii
];
1999 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
2000 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
2003 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2006 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2011 struct do_not_register
*
2012 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2014 struct do_not_register
*dnr
;
2017 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
2021 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
2023 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
2024 if(match_ircglob(chan_name
, iter_key(it
)))
2025 return iter_data(it
);
2030 static CHANSERV_FUNC(cmd_noregister
)
2033 struct do_not_register
*dnr
;
2034 char buf
[INTERVALLEN
];
2035 unsigned int matches
;
2041 reply("CSMSG_DNR_SEARCH_RESULTS");
2042 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2045 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
2047 dnr
= iter_data(it
);
2049 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2051 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2054 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
2056 dnr
= iter_data(it
);
2058 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2060 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2063 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
2065 dnr
= iter_data(it
);
2067 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2069 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2074 reply("MSG_MATCH_COUNT", matches
);
2076 reply("MSG_NO_MATCHES");
2082 if(!IsChannelName(target
) && (*target
!= '*'))
2084 reply("CSMSG_NOT_DNR", target
);
2090 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
2091 if((*target
== '*') && !get_handle_info(target
+ 1))
2093 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2096 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
2097 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2101 reply("CSMSG_DNR_SEARCH_RESULTS");
2102 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2105 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2107 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2109 reply("MSG_NO_MATCHES");
2113 static CHANSERV_FUNC(cmd_allowregister
)
2115 const char *chan_name
= argv
[1];
2117 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
2119 dict_remove(handle_dnrs
, chan_name
+1);
2120 reply("CSMSG_DNR_REMOVED", chan_name
);
2122 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
2124 dict_remove(plain_dnrs
, chan_name
);
2125 reply("CSMSG_DNR_REMOVED", chan_name
);
2127 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
2129 dict_remove(mask_dnrs
, chan_name
);
2130 reply("CSMSG_DNR_REMOVED", chan_name
);
2134 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2141 chanserv_get_owned_count(struct handle_info
*hi
)
2143 struct userData
*cList
;
2146 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2147 if(cList
->access
== UL_OWNER
)
2152 static CHANSERV_FUNC(cmd_register
)
2154 struct handle_info
*handle
;
2155 struct chanData
*cData
;
2156 struct modeNode
*mn
;
2157 char reason
[MAXLEN
];
2159 unsigned int new_channel
, force
=0;
2160 struct do_not_register
*dnr
;
2163 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2164 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2170 if(channel
->channel_info
)
2172 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2176 if(channel
->bad_channel
)
2178 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2182 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2184 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2189 chan_name
= channel
->name
;
2195 reply("MSG_MISSING_PARAMS", cmd
->name
);
2196 svccmd_send_help_brief(user
, chanserv
, cmd
);
2199 if(!IsChannelName(argv
[1]))
2201 reply("MSG_NOT_CHANNEL_NAME");
2205 if(opserv_bad_channel(argv
[1]))
2207 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2212 chan_name
= argv
[1];
2215 if(argc
>= (new_channel
+2))
2217 if(!IsHelping(user
))
2219 reply("CSMSG_PROXY_FORBIDDEN");
2223 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2225 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2226 dnr
= chanserv_is_dnr(chan_name
, handle
);
2228 /* Check if they are over the limit.. */
2229 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2231 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2238 handle
= user
->handle_info
;
2239 dnr
= chanserv_is_dnr(chan_name
, handle
);
2240 /* Check if they are over the limit.. */
2241 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2243 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2246 /* Check if another service is in the channel */
2248 for(n
= 0; n
< channel
->members
.used
; n
++)
2250 mn
= channel
->members
.list
[n
];
2251 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2253 reply("CSMSG_ANOTHER_SERVICE");
2260 if(!IsHelping(user
))
2261 reply("CSMSG_DNR_CHANNEL", chan_name
);
2263 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2267 /* now handled above for message specilization *
2268 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2270 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2276 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2278 cData
= register_channel(channel
, user
->handle_info
->handle
);
2279 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2280 cData
->modes
= chanserv_conf
.default_modes
;
2282 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2283 if (IsOffChannel(cData
))
2285 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2289 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2290 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2291 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2293 mod_chanmode_announce(chanserv
, channel
, change
);
2294 mod_chanmode_free(change
);
2297 /* Initialize the channel's max user record. */
2298 cData
->max
= channel
->members
.used
;
2299 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2301 if(handle
!= user
->handle_info
)
2302 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2305 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2306 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2307 handle
->handle
, user
->handle_info
->handle
);
2312 make_confirmation_string(struct userData
*uData
)
2314 static char strbuf
[16];
2319 for(src
= uData
->handle
->handle
; *src
; )
2320 accum
= accum
* 31 + toupper(*src
++);
2322 for(src
= uData
->channel
->channel
->name
; *src
; )
2323 accum
= accum
* 31 + toupper(*src
++);
2324 sprintf(strbuf
, "%08x", accum
);
2328 static CHANSERV_FUNC(cmd_unregister
)
2331 char reason
[MAXLEN
];
2332 struct chanData
*cData
;
2333 struct userData
*uData
;
2335 cData
= channel
->channel_info
;
2338 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2342 uData
= GetChannelUser(cData
, user
->handle_info
);
2343 if(!uData
|| (uData
->access
< UL_OWNER
))
2345 reply("CSMSG_NO_ACCESS");
2349 if(IsProtected(cData
))
2351 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2355 if(!IsHelping(user
))
2357 const char *confirm_string
;
2358 if(IsSuspended(cData
))
2360 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2363 confirm_string
= make_confirmation_string(uData
);
2364 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2366 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2371 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2372 name
= strdup(channel
->name
);
2373 unregister_channel(cData
, reason
);
2374 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2375 reply("CSMSG_UNREG_SUCCESS", name
);
2381 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2383 extern struct userNode
*spamserv
;
2384 struct mod_chanmode
*change
;
2386 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2388 change
= mod_chanmode_alloc(2);
2390 change
->args
[0].mode
= MODE_CHANOP
;
2391 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2392 change
->args
[1].mode
= MODE_CHANOP
;
2393 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2397 change
= mod_chanmode_alloc(1);
2399 change
->args
[0].mode
= MODE_CHANOP
;
2400 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2403 mod_chanmode_announce(chanserv
, channel
, change
);
2404 mod_chanmode_free(change
);
2407 static CHANSERV_FUNC(cmd_move
)
2409 struct mod_chanmode change
;
2410 struct chanNode
*target
;
2411 struct modeNode
*mn
;
2412 struct userData
*uData
;
2413 struct do_not_register
*dnr
;
2414 int chanserv_join
= 0, spamserv_join
;
2418 if(IsProtected(channel
->channel_info
))
2420 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2424 if(!IsChannelName(argv
[1]))
2426 reply("MSG_NOT_CHANNEL_NAME");
2430 if(opserv_bad_channel(argv
[1]))
2432 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2436 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2438 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2440 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2442 if(!IsHelping(user
))
2443 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2445 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2451 mod_chanmode_init(&change
);
2452 if(!(target
= GetChannel(argv
[1])))
2454 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2455 if(!IsSuspended(channel
->channel_info
))
2458 else if(target
->channel_info
)
2460 reply("CSMSG_ALREADY_REGGED", target
->name
);
2463 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2464 && !IsHelping(user
))
2466 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2469 else if(!IsSuspended(channel
->channel_info
))
2474 /* Clear MODE_REGISTERED from old channel, add it to new. */
2476 change
.modes_clear
= MODE_REGISTERED
;
2477 mod_chanmode_announce(chanserv
, channel
, &change
);
2478 change
.modes_clear
= 0;
2479 change
.modes_set
= MODE_REGISTERED
;
2480 mod_chanmode_announce(chanserv
, target
, &change
);
2483 /* Move the channel_info to the target channel; it
2484 shouldn't be necessary to clear timeq callbacks
2485 for the old channel. */
2486 target
->channel_info
= channel
->channel_info
;
2487 target
->channel_info
->channel
= target
;
2488 channel
->channel_info
= NULL
;
2490 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2493 ss_cs_join_channel(target
, spamserv_join
);
2495 if(!IsSuspended(target
->channel_info
))
2497 char reason2
[MAXLEN
];
2498 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2499 DelChannelUser(chanserv
, channel
, reason2
, 0);
2502 UnlockChannel(channel
);
2503 LockChannel(target
);
2504 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2505 channel
->name
, target
->name
, user
->handle_info
->handle
);
2507 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2512 merge_users(struct chanData
*source
, struct chanData
*target
)
2514 struct userData
*suData
, *tuData
, *next
;
2520 /* Insert the source's users into the scratch area. */
2521 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2522 dict_insert(merge
, suData
->handle
->handle
, suData
);
2524 /* Iterate through the target's users, looking for
2525 users common to both channels. The lower access is
2526 removed from either the scratch area or target user
2528 for(tuData
= target
->users
; tuData
; tuData
= next
)
2530 struct userData
*choice
;
2532 next
= tuData
->next
;
2534 /* If a source user exists with the same handle as a target
2535 channel's user, resolve the conflict by removing one. */
2536 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2540 /* Pick the data we want to keep. */
2541 /* If the access is the same, use the later seen time. */
2542 if(suData
->access
== tuData
->access
)
2543 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2544 else /* Otherwise, keep the higher access level. */
2545 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2547 /* Remove the user that wasn't picked. */
2548 if(choice
== tuData
)
2550 dict_remove(merge
, suData
->handle
->handle
);
2551 del_channel_user(suData
, 0);
2554 del_channel_user(tuData
, 0);
2557 /* Move the remaining users to the target channel. */
2558 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2560 suData
= iter_data(it
);
2562 /* Insert the user into the target channel's linked list. */
2563 suData
->prev
= NULL
;
2564 suData
->next
= target
->users
;
2565 suData
->channel
= target
;
2568 target
->users
->prev
= suData
;
2569 target
->users
= suData
;
2571 /* Update the user counts for the target channel; the
2572 source counts are left alone. */
2573 target
->userCount
++;
2576 /* Possible to assert (source->users == NULL) here. */
2577 source
->users
= NULL
;
2582 merge_bans(struct chanData
*source
, struct chanData
*target
)
2584 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2586 /* Hold on to the original head of the target ban list
2587 to avoid comparing source bans with source bans. */
2588 tFront
= target
->bans
;
2590 /* Perform a totally expensive O(n*m) merge, ick. */
2591 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2593 /* Flag to track whether the ban's been moved
2594 to the destination yet. */
2597 /* Possible to assert (sbData->prev == NULL) here. */
2598 sNext
= sbData
->next
;
2600 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2602 tNext
= tbData
->next
;
2604 /* Perform two comparisons between each source
2605 and target ban, conflicts are resolved by
2606 keeping the broader ban and copying the later
2607 expiration and triggered time. */
2608 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2610 /* There is a broader ban in the target channel that
2611 overrides one in the source channel; remove the
2612 source ban and break. */
2613 if(sbData
->expires
> tbData
->expires
)
2614 tbData
->expires
= sbData
->expires
;
2615 if(sbData
->triggered
> tbData
->triggered
)
2616 tbData
->triggered
= sbData
->triggered
;
2617 del_channel_ban(sbData
);
2620 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2622 /* There is a broader ban in the source channel that
2623 overrides one in the target channel; remove the
2624 target ban, fall through and move the source over. */
2625 if(tbData
->expires
> sbData
->expires
)
2626 sbData
->expires
= tbData
->expires
;
2627 if(tbData
->triggered
> sbData
->triggered
)
2628 sbData
->triggered
= tbData
->triggered
;
2629 if(tbData
== tFront
)
2631 del_channel_ban(tbData
);
2634 /* Source bans can override multiple target bans, so
2635 we allow a source to run through this loop multiple
2636 times, but we can only move it once. */
2641 /* Remove the source ban from the source ban list. */
2643 sbData
->next
->prev
= sbData
->prev
;
2645 /* Modify the source ban's associated channel. */
2646 sbData
->channel
= target
;
2648 /* Insert the ban into the target channel's linked list. */
2649 sbData
->prev
= NULL
;
2650 sbData
->next
= target
->bans
;
2653 target
->bans
->prev
= sbData
;
2654 target
->bans
= sbData
;
2656 /* Update the user counts for the target channel. */
2661 /* Possible to assert (source->bans == NULL) here. */
2662 source
->bans
= NULL
;
2666 merge_data(struct chanData
*source
, struct chanData
*target
)
2668 /* Use more recent visited and owner-transfer time; use older
2669 * registered time. Bitwise or may_opchan. Use higher max.
2670 * Do not touch last_refresh, ban count or user counts.
2672 if(source
->visited
> target
->visited
)
2673 target
->visited
= source
->visited
;
2674 if(source
->registered
< target
->registered
)
2675 target
->registered
= source
->registered
;
2676 if(source
->ownerTransfer
> target
->ownerTransfer
)
2677 target
->ownerTransfer
= source
->ownerTransfer
;
2678 if(source
->may_opchan
)
2679 target
->may_opchan
= 1;
2680 if(source
->max
> target
->max
)
2681 target
->max
= source
->max
;
2685 merge_channel(struct chanData
*source
, struct chanData
*target
)
2687 merge_users(source
, target
);
2688 merge_bans(source
, target
);
2689 merge_data(source
, target
);
2692 static CHANSERV_FUNC(cmd_merge
)
2694 struct userData
*target_user
;
2695 struct chanNode
*target
;
2696 char reason
[MAXLEN
];
2700 /* Make sure the target channel exists and is registered to the user
2701 performing the command. */
2702 if(!(target
= GetChannel(argv
[1])))
2704 reply("MSG_INVALID_CHANNEL");
2708 if(!target
->channel_info
)
2710 reply("CSMSG_NOT_REGISTERED", target
->name
);
2714 if(IsProtected(channel
->channel_info
))
2716 reply("CSMSG_MERGE_NODELETE");
2720 if(IsSuspended(target
->channel_info
))
2722 reply("CSMSG_MERGE_SUSPENDED");
2726 if(channel
== target
)
2728 reply("CSMSG_MERGE_SELF");
2732 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2733 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2735 reply("CSMSG_MERGE_NOT_OWNER");
2739 /* Merge the channel structures and associated data. */
2740 merge_channel(channel
->channel_info
, target
->channel_info
);
2741 spamserv_cs_move_merge(user
, channel
, target
, 0);
2742 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2743 unregister_channel(channel
->channel_info
, reason
);
2744 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2748 static CHANSERV_FUNC(cmd_opchan
)
2750 struct mod_chanmode change
;
2751 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2753 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2756 if(!IsInChannel(channel
,chanserv
)) {
2757 reply("CSMSG_NOT_IN_CHANNEL", channel
->name
);
2760 channel
->channel_info
->may_opchan
= 0;
2761 mod_chanmode_init(&change
);
2763 change
.args
[0].mode
= MODE_CHANOP
;
2764 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2765 mod_chanmode_announce(chanserv
, channel
, &change
);
2766 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2770 static CHANSERV_FUNC(cmd_adduser
)
2772 struct userData
*actee
;
2773 struct userData
*actor
;
2774 struct handle_info
*handle
;
2775 unsigned short access
;
2779 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2781 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2785 access
= user_level_from_name(argv
[2], UL_OWNER
);
2788 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2792 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2793 if(actor
->access
<= access
)
2795 reply("CSMSG_NO_BUMP_ACCESS");
2799 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2801 // 'kevin must first authenticate with AuthServ.' is sent to user
2802 struct userNode
*unode
;
2803 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2806 if(find_adduser_pending(channel
, unode
)) {
2807 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2810 if(IsInChannel(channel
, unode
)) {
2811 reply("CSMSG_ADDUSER_PENDING");
2812 add_adduser_pending(channel
, unode
, access
);
2813 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2815 /* this results in user must auth AND not in chan errors. too confusing..
2817 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2825 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2827 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2831 time_t accessexpiry
= 0;
2832 unsigned int duration
= 0;
2834 if ((duration
= ParseInterval(argv
[3])))
2835 accessexpiry
= now
+ duration
;
2838 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
, accessexpiry
);
2839 scan_user_presence(actee
, NULL
);
2842 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
2844 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2848 static CHANSERV_FUNC(cmd_clvl
)
2850 struct handle_info
*handle
;
2851 struct userData
*victim
;
2852 struct userData
*actor
;
2853 unsigned short new_access
;
2854 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2858 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2860 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2863 if(handle
== user
->handle_info
&& !privileged
)
2865 reply("CSMSG_NO_SELF_CLVL");
2869 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2871 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2875 if(actor
->access
<= victim
->access
&& !privileged
)
2877 reply("MSG_USER_OUTRANKED", handle
->handle
);
2881 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2885 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2889 if(new_access
>= actor
->access
&& !privileged
)
2891 reply("CSMSG_NO_BUMP_ACCESS");
2895 time_t clvlexpiry
= 0;
2896 unsigned int duration
= 0;
2898 if ((duration
= ParseInterval(argv
[3])))
2899 clvlexpiry
= now
+ duration
;
2903 if (victim
->accessexpiry
> 0) {
2904 reply("CSMSG_NO_BUMP_EXPIRY");
2908 victim
->clvlexpiry
= clvlexpiry
;
2909 victim
->lastaccess
= victim
->access
;
2910 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
2913 victim
->access
= new_access
;
2914 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2918 static CHANSERV_FUNC(cmd_deluser
)
2920 struct handle_info
*handle
;
2921 struct userData
*victim
;
2922 struct userData
*actor
;
2923 unsigned short access
;
2928 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2930 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2933 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2935 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2941 access
= user_level_from_name(argv
[1], UL_OWNER
);
2942 char *useraccess
= user_level_name_from_level(victim
->access
);
2945 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2948 if(strcasecmp(argv
[1], useraccess
))
2950 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2956 access
= victim
->access
;
2959 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2961 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2965 chan_name
= strdup(channel
->name
);
2966 del_channel_user(victim
, 1);
2967 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2973 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2975 struct userData
*actor
, *uData
, *next
;
2977 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2979 if(min_access
> max_access
)
2981 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2985 if((actor
->access
<= max_access
) && !IsHelping(user
))
2987 reply("CSMSG_NO_ACCESS");
2991 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2995 if((uData
->access
>= min_access
)
2996 && (uData
->access
<= max_access
)
2997 && match_ircglob(uData
->handle
->handle
, mask
))
2998 del_channel_user(uData
, 1);
3001 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3005 static CHANSERV_FUNC(cmd_mdelowner
)
3007 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3010 static CHANSERV_FUNC(cmd_mdelcoowner
)
3012 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3015 static CHANSERV_FUNC(cmd_mdelmanager
)
3017 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3020 static CHANSERV_FUNC(cmd_mdelop
)
3022 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3025 static CHANSERV_FUNC(cmd_mdelhalfop
)
3027 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3030 static CHANSERV_FUNC(cmd_mdelpeon
)
3032 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3035 static CHANSERV_FUNC(cmd_mdelpal
)
3037 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3040 static CHANSERV_FUNC(cmd_levels
)
3042 struct helpfile_table tbl
;
3045 tbl
.length
= 6 + 1; // 6 levels
3048 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3049 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3050 tbl
.contents
[0][0] = "Level";
3051 tbl
.contents
[0][1] = "From";
3052 tbl
.contents
[0][2] = "-";
3053 tbl
.contents
[0][3] = "To";
3055 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3056 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3057 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3058 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3059 tbl
.contents
[ii
][3] = msnprintf(1, "");
3061 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3062 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3063 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3064 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3065 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3067 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3068 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3069 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3070 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3071 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3073 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3074 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3075 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3076 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3077 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3079 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3080 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3081 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3082 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3083 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-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_PEON
));
3087 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3088 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3089 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3091 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3095 reply("CSMSG_LEVELS_HEADER");
3096 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3097 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3098 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3099 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3100 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3101 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3108 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3110 struct banData
*bData
, *next
;
3111 char interval
[INTERVALLEN
];
3116 limit
= now
- duration
;
3117 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3121 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3124 del_channel_ban(bData
);
3128 intervalString(interval
, duration
, user
->handle_info
);
3129 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3134 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
)
3136 struct userData
*actor
, *uData
, *next
;
3137 char interval
[INTERVALLEN
];
3141 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3142 if(min_access
> max_access
)
3144 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3148 if((actor
->access
<= max_access
) && !IsHelping(user
))
3150 reply("CSMSG_NO_ACCESS");
3155 limit
= now
- duration
;
3156 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3160 if((uData
->seen
> limit
)
3162 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3165 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3166 || (!max_access
&& (uData
->access
< actor
->access
)))
3168 del_channel_user(uData
, 1);
3176 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3178 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3182 static CHANSERV_FUNC(cmd_trim
)
3184 unsigned long duration
;
3185 unsigned short min_level
, max_level
;
3190 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3191 duration
= ParseInterval(argv
[2]);
3194 reply("CSMSG_CANNOT_TRIM");
3198 if(!irccasecmp(argv
[1], "lamers"))
3200 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3203 else if(!irccasecmp(argv
[1], "users"))
3205 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3208 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3210 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3213 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3215 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3220 reply("CSMSG_INVALID_TRIM", argv
[1]);
3225 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3226 to the user. cmd_all takes advantage of this. */
3227 static CHANSERV_FUNC(cmd_up
)
3229 struct mod_chanmode change
;
3230 struct userData
*uData
;
3233 mod_chanmode_init(&change
);
3235 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3236 if(!change
.args
[0].u
.member
)
3239 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3243 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3247 reply("CSMSG_GODMODE_UP", argv
[0]);
3250 else if(uData
->access
>= UL_OP
)
3252 change
.args
[0].mode
= MODE_CHANOP
;
3253 errmsg
= "CSMSG_ALREADY_OPPED";
3255 else if(uData
->access
>= UL_HALFOP
)
3257 change
.args
[0].mode
= MODE_HALFOP
;
3258 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3260 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3262 change
.args
[0].mode
= MODE_VOICE
;
3263 errmsg
= "CSMSG_ALREADY_VOICED";
3268 reply("CSMSG_NO_ACCESS");
3271 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3272 if(!change
.args
[0].mode
)
3275 reply(errmsg
, channel
->name
);
3278 modcmd_chanmode_announce(&change
);
3282 static CHANSERV_FUNC(cmd_down
)
3284 struct mod_chanmode change
;
3286 mod_chanmode_init(&change
);
3288 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3289 if(!change
.args
[0].u
.member
)
3292 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3296 if(!change
.args
[0].u
.member
->modes
)
3299 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3303 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3304 modcmd_chanmode_announce(&change
);
3308 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
)
3310 struct userData
*cList
;
3312 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3314 if(IsSuspended(cList
->channel
)
3315 || IsUserSuspended(cList
)
3316 || !GetUserMode(cList
->channel
->channel
, user
))
3319 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3325 static CHANSERV_FUNC(cmd_upall
)
3327 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3330 static CHANSERV_FUNC(cmd_downall
)
3332 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3335 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3336 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3339 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
)
3341 unsigned int ii
, valid
;
3342 struct userNode
*victim
;
3343 struct mod_chanmode
*change
;
3345 change
= mod_chanmode_alloc(argc
- 1);
3347 for(ii
=valid
=0; ++ii
< argc
; )
3349 if(!(victim
= GetUserH(argv
[ii
])))
3351 change
->args
[valid
].mode
= mode
;
3352 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3353 if(!change
->args
[valid
].u
.member
)
3355 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3360 change
->argc
= valid
;
3361 if(valid
< (argc
-1))
3362 reply("CSMSG_PROCESS_FAILED");
3365 modcmd_chanmode_announce(change
);
3366 reply(action
, channel
->name
);
3368 mod_chanmode_free(change
);
3372 static CHANSERV_FUNC(cmd_op
)
3374 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3377 static CHANSERV_FUNC(cmd_hop
)
3379 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3382 static CHANSERV_FUNC(cmd_deop
)
3384 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3387 static CHANSERV_FUNC(cmd_dehop
)
3389 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3392 static CHANSERV_FUNC(cmd_voice
)
3394 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3397 static CHANSERV_FUNC(cmd_devoice
)
3399 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3403 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3409 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3411 struct modeNode
*mn
= channel
->members
.list
[ii
];
3413 if(IsService(mn
->user
))
3416 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3419 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3423 victims
[(*victimCount
)++] = mn
;
3429 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3431 struct userNode
*victim
;
3432 struct modeNode
**victims
;
3433 unsigned int offset
, n
, victimCount
, duration
= 0;
3434 char *reason
= "Bye.", *ban
, *name
;
3435 char interval
[INTERVALLEN
];
3437 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3438 REQUIRE_PARAMS(offset
);
3441 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3442 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3444 /* Truncate the reason to a length of TOPICLEN, as
3445 the ircd does; however, leave room for an ellipsis
3446 and the kicker's nick. */
3447 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3451 if((victim
= GetUserH(argv
[1])))
3453 victims
= alloca(sizeof(victims
[0]));
3454 victims
[0] = GetUserMode(channel
, victim
);
3455 /* XXX: The comparison with ACTION_KICK is just because all
3456 * other actions can work on users outside the channel, and we
3457 * want to allow those (e.g. unbans) in that case. If we add
3458 * some other ejection action for in-channel users, change
3460 victimCount
= victims
[0] ? 1 : 0;
3462 if(IsService(victim
))
3465 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3469 if((action
== ACTION_KICK
) && !victimCount
)
3472 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3476 if(protect_user(victim
, user
, channel
->channel_info
, false))
3478 // This translates to send_message(user, cmd->parent->bot, ...)
3479 // if user is x3 (ctcp action) cmd is null and segfault.
3481 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3485 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3486 name
= victim
->nick
;
3490 if(!is_ircmask(argv
[1]))
3493 reply("MSG_NICK_UNKNOWN", argv
[1]);
3497 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3499 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3502 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3505 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3506 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3508 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3509 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3510 some creativity, but its not x3's job to be the ban censor anyway. */
3511 if(is_overmask(argv
[1]))
3514 reply("CSMSG_LAME_MASK", argv
[1]);
3518 if((action
== ACTION_KICK
) && (victimCount
== 0))
3521 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3525 name
= ban
= strdup(argv
[1]);
3528 /* Truncate the ban in place if necessary; we must ensure
3529 that 'ban' is a valid ban mask before sanitizing it. */
3530 sanitize_ircmask(ban
);
3532 if(action
& ACTION_ADD_LAMER
)
3534 struct banData
*bData
, *next
;
3536 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3539 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3544 if(action
& ACTION_ADD_TIMED_LAMER
)
3546 duration
= ParseInterval(argv
[2]);
3551 reply("CSMSG_DURATION_TOO_LOW");
3555 else if(duration
> (86400 * 365 * 2))
3558 reply("CSMSG_DURATION_TOO_HIGH");
3565 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3567 if(match_ircglobs(bData
->mask
, ban
))
3569 int exact
= !irccasecmp(bData
->mask
, ban
);
3571 /* The ban is redundant; there is already a ban
3572 with the same effect in place. */
3576 free(bData
->reason
);
3577 bData
->reason
= strdup(reason
);
3578 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3580 reply("CSMSG_REASON_CHANGE", ban
);
3584 if(exact
&& bData
->expires
)
3588 /* If the ban matches an existing one exactly,
3589 extend the expiration time if the provided
3590 duration is longer. */
3591 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3593 bData
->expires
= now
+ duration
;
3604 /* Delete the expiration timeq entry and
3605 requeue if necessary. */
3606 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3609 timeq_add(bData
->expires
, expire_ban
, bData
);
3613 /* automated kickban, dont reply */
3616 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3618 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3624 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3631 if(match_ircglobs(ban
, bData
->mask
))
3633 /* The ban we are adding makes previously existing
3634 bans redundant; silently remove them. */
3635 del_channel_ban(bData
);
3639 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
);
3641 name
= ban
= strdup(bData
->mask
);
3645 /* WHAT DOES THIS DO?? -Rubin */
3646 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3648 extern const char *hidden_host_suffix
;
3649 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3651 unsigned int l1
, l2
;
3654 l2
= strlen(old_name
);
3657 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3659 new_mask
= malloc(MAXLEN
);
3660 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3662 name
= ban
= new_mask
;
3667 if(action
& ACTION_BAN
)
3669 unsigned int exists
;
3670 struct mod_chanmode
*change
;
3672 if(channel
->banlist
.used
>= MAXBANS
)
3675 reply("CSMSG_BANLIST_FULL", channel
->name
);
3680 exists
= ChannelBanExists(channel
, ban
);
3681 change
= mod_chanmode_alloc(victimCount
+ 1);
3682 for(n
= 0; n
< victimCount
; ++n
)
3684 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3685 change
->args
[n
].u
.member
= victims
[n
];
3689 change
->args
[n
].mode
= MODE_BAN
;
3690 change
->args
[n
++].u
.hostmask
= ban
;
3694 modcmd_chanmode_announce(change
);
3696 mod_chanmode_announce(chanserv
, channel
, change
);
3697 mod_chanmode_free(change
);
3699 if(exists
&& (action
== ACTION_BAN
))
3702 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3708 if(action
& ACTION_ADD_LAMER
)
3710 char kick_reason
[MAXLEN
];
3711 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3713 for(n
= 0; n
< victimCount
; n
++) {
3714 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3715 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3719 else if(action
& ACTION_KICK
)
3721 char kick_reason
[MAXLEN
];
3722 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3724 for(n
= 0; n
< victimCount
; n
++) {
3725 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3731 /* No response, since it was automated. */
3733 else if(action
& ACTION_ADD_LAMER
)
3736 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3738 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3740 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3741 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3742 else if(action
& ACTION_BAN
)
3743 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3744 else if(action
& ACTION_KICK
&& victimCount
)
3745 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3751 static CHANSERV_FUNC(cmd_kickban
)
3753 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3756 static CHANSERV_FUNC(cmd_kick
)
3758 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3761 static CHANSERV_FUNC(cmd_ban
)
3763 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3766 static CHANSERV_FUNC(cmd_addlamer
)
3768 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3771 static CHANSERV_FUNC(cmd_addtimedlamer
)
3773 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3776 static struct mod_chanmode
*
3777 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3779 struct mod_chanmode
*change
;
3780 unsigned char *match
;
3781 unsigned int ii
, count
;
3783 match
= alloca(bans
->used
);
3786 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3788 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3789 MATCH_USENICK
| MATCH_VISIBLE
);
3796 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3798 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3805 change
= mod_chanmode_alloc(count
);
3806 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3810 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3811 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3813 assert(count
== change
->argc
);
3817 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3819 unsigned int jj
, ii
, count
;
3821 struct chanData
*channel
;
3823 struct mod_chanmode
*change
;
3825 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3826 /* Walk through every channel */
3827 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3828 switch(channel
->chOpts
[chBanTimeout
])
3830 default: case '0': continue; /* Dont remove bans in this chan */
3831 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3832 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3833 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3834 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3835 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3838 /* First find out how many bans were going to unset */
3839 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3840 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3844 /* At least one ban, so setup a removal */
3845 change
= mod_chanmode_alloc(count
);
3847 /* Walk over every ban in this channel.. */
3848 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3849 bn
= channel
->channel
->banlist
.list
[jj
];
3850 if (bn
->set
< bantimeout
) {
3851 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3853 /* Add this ban to the mode change */
3854 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3855 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3857 /* Pull this ban out of the list */
3858 banList_remove(&(channel
->channel
->banlist
), bn
);
3863 /* Send the modes to IRC */
3864 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3866 /* free memory from strdup above */
3867 for(ii
= 0; ii
< count
; ++ii
)
3868 free((char*)change
->args
[ii
].u
.hostmask
);
3870 mod_chanmode_free(change
);
3873 /* Set this function to run again */
3874 if(chanserv_conf
.ban_timeout_frequency
)
3875 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3880 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3882 struct userNode
*actee
;
3888 /* may want to allow a comma delimited list of users... */
3889 if(!(actee
= GetUserH(argv
[1])))
3891 if(!is_ircmask(argv
[1]))
3893 reply("MSG_NICK_UNKNOWN", argv
[1]);
3897 mask
= strdup(argv
[1]);
3900 /* We don't sanitize the mask here because ircu
3902 if(action
& ACTION_UNBAN
)
3904 struct mod_chanmode
*change
;
3905 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3910 modcmd_chanmode_announce(change
);
3911 for(ii
= 0; ii
< change
->argc
; ++ii
)
3912 free((char*)change
->args
[ii
].u
.hostmask
);
3913 mod_chanmode_free(change
);
3918 if(action
& ACTION_DEL_LAMER
)
3920 struct banData
*ban
, *next
;
3922 ban
= channel
->channel_info
->bans
; /* lamers */
3926 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3929 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3934 del_channel_ban(ban
);
3941 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3943 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3949 static CHANSERV_FUNC(cmd_unban
)
3951 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3954 static CHANSERV_FUNC(cmd_dellamer
)
3956 /* it doesn't necessarily have to remove the channel ban - may want
3957 to make that an option. */
3958 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3961 static CHANSERV_FUNC(cmd_unbanme
)
3963 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3964 long flags
= ACTION_UNBAN
;
3966 /* remove permanent bans if the user has the proper access. */
3967 if(uData
->access
>= UL_MANAGER
)
3968 flags
|= ACTION_DEL_LAMER
;
3970 argv
[1] = user
->nick
;
3971 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3974 static CHANSERV_FUNC(cmd_unbanall
)
3976 struct mod_chanmode
*change
;
3979 if(!channel
->banlist
.used
)
3981 reply("CSMSG_NO_BANS", channel
->name
);
3985 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3986 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3988 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3989 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3991 modcmd_chanmode_announce(change
);
3992 for(ii
= 0; ii
< change
->argc
; ++ii
)
3993 free((char*)change
->args
[ii
].u
.hostmask
);
3994 mod_chanmode_free(change
);
3995 reply("CSMSG_BANS_REMOVED", channel
->name
);
3999 static CHANSERV_FUNC(cmd_open
)
4001 struct mod_chanmode
*change
;
4004 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4006 change
= mod_chanmode_alloc(0);
4007 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4008 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4009 && channel
->channel_info
->modes
.modes_set
)
4010 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4011 modcmd_chanmode_announce(change
);
4012 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4013 for(ii
= 0; ii
< change
->argc
; ++ii
)
4014 free((char*)change
->args
[ii
].u
.hostmask
);
4015 mod_chanmode_free(change
);
4019 static CHANSERV_FUNC(cmd_myaccess
)
4021 static struct string_buffer sbuf
;
4022 struct handle_info
*target_handle
;
4023 struct userData
*uData
;
4026 target_handle
= user
->handle_info
;
4027 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4029 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
4031 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4034 if(!target_handle
->channels
)
4036 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4040 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4041 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4043 struct chanData
*cData
= uData
->channel
;
4045 if(uData
->access
> UL_OWNER
)
4047 if(IsProtected(cData
)
4048 && (target_handle
!= user
->handle_info
)
4049 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4052 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4053 if(uData
->flags
== USER_AUTO_OP
)
4054 string_buffer_append(&sbuf
, ',');
4055 if(IsUserSuspended(uData
))
4056 string_buffer_append(&sbuf
, 's');
4057 if(IsUserAutoOp(uData
))
4059 if(uData
->access
>= UL_OP
)
4060 string_buffer_append(&sbuf
, 'o');
4061 else if(uData
->access
>= UL_HALFOP
)
4062 string_buffer_append(&sbuf
, 'h');
4063 else if(uData
->access
>= UL_PEON
)
4064 string_buffer_append(&sbuf
, 'v');
4066 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4067 string_buffer_append(&sbuf
, 'i');
4068 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4069 string_buffer_append(&sbuf
, 'j');
4071 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4073 string_buffer_append_string(&sbuf
, ")]");
4074 string_buffer_append(&sbuf
, '\0');
4075 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4081 static CHANSERV_FUNC(cmd_access
)
4083 struct userNode
*target
;
4084 struct handle_info
*target_handle
;
4085 struct userData
*uData
;
4087 char prefix
[MAXLEN
];
4092 target_handle
= target
->handle_info
;
4094 else if((target
= GetUserH(argv
[1])))
4096 target_handle
= target
->handle_info
;
4098 else if(argv
[1][0] == '*')
4100 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4102 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4108 reply("MSG_NICK_UNKNOWN", argv
[1]);
4112 assert(target
|| target_handle
);
4114 if(target
== chanserv
)
4116 reply("CSMSG_IS_CHANSERV");
4124 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4129 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4132 reply("MSG_AUTHENTICATE");
4138 const char *epithet
= NULL
, *type
= NULL
;
4141 epithet
= chanserv_conf
.irc_operator_epithet
;
4144 else if(IsNetworkHelper(target
))
4146 epithet
= chanserv_conf
.network_helper_epithet
;
4147 type
= "network helper";
4149 else if(IsSupportHelper(target
))
4151 epithet
= chanserv_conf
.support_helper_epithet
;
4152 type
= "support helper";
4156 if(target_handle
->epithet
)
4157 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4159 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4161 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4165 sprintf(prefix
, "%s", target_handle
->handle
);
4168 if(!channel
->channel_info
)
4170 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4174 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4175 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4176 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4178 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4179 /* To prevent possible information leaks, only show infolines
4180 * if the requestor is in the channel or it's their own
4182 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4184 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4186 /* Likewise, only say it's suspended if the user has active
4187 * access in that channel or it's their own entry. */
4188 if(IsUserSuspended(uData
)
4189 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4190 || (user
->handle_info
== uData
->handle
)))
4192 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4197 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4203 /* This is never used...
4205 zoot_list(struct listData *list)
4207 struct userData *uData;
4208 unsigned int start, curr, highest, lowest;
4209 struct helpfile_table tmp_table;
4210 const char **temp, *msg;
4212 if(list->table.length == 1)
4215 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);
4217 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));
4218 msg = user_find_message(list->user, "MSG_NONE");
4219 send_message_type(4, list->user, list->bot, " %s", msg);
4221 tmp_table.width = list->table.width;
4222 tmp_table.flags = list->table.flags;
4223 list->table.contents[0][0] = " ";
4224 highest = list->highest;
4225 if(list->lowest != 0)
4226 lowest = list->lowest;
4227 else if(highest < 100)
4230 lowest = highest - 100;
4231 for(start = curr = 1; curr < list->table.length; )
4233 uData = list->users[curr-1];
4234 list->table.contents[curr++][0] = " ";
4235 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4238 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);
4240 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));
4241 temp = list->table.contents[--start];
4242 list->table.contents[start] = list->table.contents[0];
4243 tmp_table.contents = list->table.contents + start;
4244 tmp_table.length = curr - start;
4245 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4246 list->table.contents[start] = temp;
4248 highest = lowest - 1;
4249 lowest = (highest < 100) ? 0 : (highest - 99);
4256 normal_list(struct listData
*list
)
4260 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
);
4262 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
));
4263 if(list
->table
.length
== 1)
4265 msg
= user_find_message(list
->user
, "MSG_NONE");
4266 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4269 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4272 /* if these need changed, uncomment and customize
4274 clean_list(struct listData *list)
4278 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);
4280 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));
4281 if(list->table.length == 1)
4283 msg = user_find_message(list->user, "MSG_NONE");
4284 send_message_type(4, list->user, list->bot, " %s", msg);
4287 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4291 advanced_list(struct listData *list)
4295 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);
4297 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));
4298 if(list->table.length == 1)
4300 msg = user_find_message(list->user, "MSG_NONE");
4301 send_message_type(4, list->user, list->bot, " %s", msg);
4304 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4308 classic_list(struct listData *list)
4312 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4314 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4315 if(list->table.length == 1)
4317 msg = user_find_message(list->user, "MSG_NONE");
4318 send_message_type(4, list->user, list->bot, " %s", msg);
4321 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4326 userData_access_comp(const void *arg_a
, const void *arg_b
)
4328 const struct userData
*a
= *(struct userData
**)arg_a
;
4329 const struct userData
*b
= *(struct userData
**)arg_b
;
4331 if(a
->access
!= b
->access
)
4332 res
= b
->access
- a
->access
;
4334 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4339 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4341 void (*send_list
)(struct listData
*);
4342 struct userData
*uData
;
4343 struct listData lData
;
4344 unsigned int matches
;
4350 lData
.bot
= cmd
->parent
->bot
;
4351 lData
.channel
= channel
;
4352 lData
.lowest
= lowest
;
4353 lData
.highest
= highest
;
4354 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4355 send_list
= normal_list
;
4356 /* What does the following line do exactly?? */
4357 /*(void)zoot_list; ** since it doesn't show user levels */
4360 if(user->handle_info)
4362 switch(user->handle_info->userlist_style)
4364 case HI_STYLE_CLEAN:
4365 send_list = clean_list;
4367 case HI_STYLE_ADVANCED:
4368 send_list = advanced_list;
4370 case HI_STYLE_CLASSIC:
4371 send_list = classic_list;
4373 case HI_STYLE_NORMAL:
4375 send_list = normal_list;
4380 send_list
= normal_list
;
4382 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4384 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4386 if((uData
->access
< lowest
)
4387 || (uData
->access
> highest
)
4388 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4390 lData
.users
[matches
++] = uData
;
4392 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4394 lData
.table
.length
= matches
+1;
4395 lData
.table
.flags
= TABLE_NO_FREE
;
4396 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4398 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4399 lData
.table
.width
= 6; /* with level = 6 */
4401 lData
.table
.width
= 5; /* without = 5 */
4402 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4403 lData
.table
.contents
[0] = ary
;
4404 if(user
->handle_info
) {
4405 switch(user
->handle_info
->userlist_style
) {
4406 case HI_STYLE_CLASSIC
:
4409 case HI_STYLE_ADVANCED
:
4410 ary
[i
++] = "Access";
4413 case HI_STYLE_CLEAN
:
4414 ary
[i
++] = "Access";
4416 case HI_STYLE_NORMAL
:
4418 ary
[i
++] = "Access";
4423 ary
[i
++] = "Access";
4425 ary
[i
++] = "Account";
4426 ary
[i
] = "Last Seen";
4428 ary
[i
++] = "Status";
4429 ary
[i
++] = "Expiry";
4430 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4432 struct userData
*uData
= lData
.users
[matches
-1];
4433 char seen
[INTERVALLEN
];
4436 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4437 lData
.table
.contents
[matches
] = ary
;
4438 if(user
->handle_info
) {
4439 switch(user
->handle_info
->userlist_style
) {
4440 case HI_STYLE_CLASSIC
:
4441 ary
[i
++] = strtab(uData
->access
);
4443 case HI_STYLE_ADVANCED
:
4444 ary
[i
++] = user_level_name_from_level(uData
->access
);
4445 ary
[i
++] = strtab(uData
->access
);
4447 case HI_STYLE_CLEAN
:
4448 ary
[i
++] = user_level_name_from_level(uData
->access
);
4450 case HI_STYLE_NORMAL
:
4452 ary
[i
++] = user_level_name_from_level(uData
->access
);
4457 ary
[i
++] = user_level_name_from_level(uData
->access
);
4459 ary
[i
++] = uData
->handle
->handle
;
4462 else if(!uData
->seen
)
4465 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4466 ary
[i
] = strdup(ary
[i
]);
4468 if(IsUserSuspended(uData
))
4469 ary
[i
++] = "Suspended";
4470 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4471 ary
[i
++] = "Vacation";
4473 ary
[i
++] = "Normal";
4475 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4476 char delay
[INTERVALLEN
];
4479 if (uData
->accessexpiry
> 0) {
4480 diff
= uData
->accessexpiry
- now
;
4481 intervalString(delay
, diff
, user
->handle_info
);
4483 diff
= uData
->clvlexpiry
- now
;
4484 intervalString(delay
, diff
, user
->handle_info
);
4492 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4494 /* Free strdup above */
4495 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4496 free(lData
.table
.contents
[matches
]);
4498 free(lData
.table
.contents
[0]);
4499 free(lData
.table
.contents
);
4503 /* Remove this now that debugging is over? or improve it for
4504 * users? Would it be better tied into USERS somehow? -Rubin */
4505 static CHANSERV_FUNC(cmd_pending
)
4507 struct adduserPending
*ap
;
4508 reply("CSMSG_ADDUSER_PENDING_HEADER");
4509 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4511 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4512 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4513 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4517 static CHANSERV_FUNC(cmd_users
)
4519 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4522 static CHANSERV_FUNC(cmd_wlist
)
4524 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4527 static CHANSERV_FUNC(cmd_clist
)
4529 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4532 static CHANSERV_FUNC(cmd_mlist
)
4534 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4537 static CHANSERV_FUNC(cmd_olist
)
4539 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4542 static CHANSERV_FUNC(cmd_hlist
)
4544 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4547 static CHANSERV_FUNC(cmd_plist
)
4549 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4552 static CHANSERV_FUNC(cmd_lamers
)
4554 struct helpfile_table tbl
;
4555 unsigned int matches
= 0, timed
= 0, ii
;
4556 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4557 const char *msg_never
, *triggered
, *expires
;
4558 struct banData
*ban
, **bans
; /* lamers */
4565 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4566 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4569 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4571 if(search
&& !match_ircglobs(search
, ban
->mask
))
4573 bans
[matches
++] = ban
;
4578 tbl
.length
= matches
+ 1;
4579 tbl
.width
= 4 + timed
;
4581 tbl
.flags
= TABLE_NO_FREE
;
4582 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4583 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4584 tbl
.contents
[0][0] = "Mask";
4585 tbl
.contents
[0][1] = "Set By";
4586 tbl
.contents
[0][2] = "Triggered";
4589 tbl
.contents
[0][3] = "Expires";
4590 tbl
.contents
[0][4] = "Reason";
4593 tbl
.contents
[0][3] = "Reason";
4596 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4597 /* reply("MSG_NONE"); */
4598 free(tbl
.contents
[0]);
4603 msg_never
= user_find_message(user
, "MSG_NEVER");
4604 for(ii
= 0; ii
< matches
; )
4610 else if(ban
->expires
)
4611 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4613 expires
= msg_never
;
4616 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4618 triggered
= msg_never
;
4620 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4621 tbl
.contents
[ii
][0] = ban
->mask
;
4622 tbl
.contents
[ii
][1] = ban
->owner
;
4623 tbl
.contents
[ii
][2] = strdup(triggered
);
4626 tbl
.contents
[ii
][3] = strdup(expires
);
4627 tbl
.contents
[ii
][4] = ban
->reason
;
4630 tbl
.contents
[ii
][3] = ban
->reason
;
4632 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4633 /* reply("MSG_MATCH_COUNT", matches); */
4634 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4636 free((char*)tbl
.contents
[ii
][2]);
4638 free((char*)tbl
.contents
[ii
][3]);
4639 free(tbl
.contents
[ii
]);
4641 free(tbl
.contents
[0]);
4648 * return + if the user does NOT have the right to set the topic, and
4649 * the topic is changed.
4652 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4654 struct chanData
*cData
= channel
->channel_info
;
4655 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4657 else if(cData
->topic
)
4658 return irccasecmp(new_topic
, cData
->topic
);
4665 * Makes a givin topic fit into a givin topic mask and returns
4668 * topic_mask - the mask to conform to
4669 * topic - the topic to make conform
4670 * new_topic - the pre-allocated char* to put the new topic into
4672 * modifies: new_topic
4675 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4677 //char *topic_mask = cData->topic_mask;
4679 int pos
=0, starpos
=-1, dpos
=0, len
;
4681 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4688 strcpy(new_topic
, "");
4691 len
= strlen(topic
);
4692 if((dpos
+ len
) > TOPICLEN
)
4693 len
= TOPICLEN
+ 1 - dpos
;
4694 memcpy(new_topic
+dpos
, topic
, len
);
4698 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4699 default: new_topic
[dpos
++] = tchar
; break;
4702 if((dpos
> TOPICLEN
) || tchar
)
4704 strcpy(new_topic
, "");
4707 new_topic
[dpos
] = 0;
4711 static CHANSERV_FUNC(cmd_topic
)
4713 struct chanData
*cData
;
4717 #ifdef WITH_PROTOCOL_P10
4721 cData
= channel
->channel_info
;
4726 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4727 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4728 reply("CSMSG_TOPIC_SET", cData
->topic
);
4732 reply("CSMSG_NO_TOPIC", channel
->name
);
4736 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4737 /* If they say "!topic *", use an empty topic. */
4738 if((topic
[0] == '*') && (topic
[1] == 0))
4741 if(bad_topic(channel
, user
, topic
))
4743 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4748 /* If there is a topicmask set, and the new topic doesnt match, make it */
4749 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4751 char *topic_mask
= cData
->topic_mask
;
4752 char new_topic
[TOPICLEN
+1];
4754 /* make a new topic fitting mask */
4755 conform_topic(topic_mask
, topic
, new_topic
);
4758 /* Topic couldnt fit into mask, was too long */
4759 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4760 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4763 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4765 else /* No mask set, just set the topic */
4766 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4769 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4771 /* Grab the topic and save it as the default topic. */
4773 cData
->topic
= strdup(channel
->topic
);
4779 static CHANSERV_FUNC(cmd_mode
)
4781 struct userData
*uData
;
4782 struct mod_chanmode
*change
;
4787 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4788 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4792 change
= &channel
->channel_info
->modes
;
4793 if(change
->modes_set
|| change
->modes_clear
) {
4794 modcmd_chanmode_announce(change
);
4795 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4797 reply("CSMSG_NO_MODES", channel
->name
);
4801 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4803 base_oplevel
= MAXOPLEVEL
;
4804 else if (uData
->access
>= UL_OWNER
)
4807 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4808 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4812 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4816 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4817 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4820 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4821 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4825 modcmd_chanmode_announce(change
);
4826 mod_chanmode_free(change
);
4827 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4831 static CHANSERV_FUNC(cmd_invite
)
4833 struct userData
*uData
;
4834 struct userNode
*invite
;
4836 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4840 if(!(invite
= GetUserH(argv
[1])))
4842 reply("MSG_NICK_UNKNOWN", argv
[1]);
4849 if(GetUserMode(channel
, invite
))
4851 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4859 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4860 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4863 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4866 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4868 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4869 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4870 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4876 irc_invite(chanserv
, invite
, channel
);
4878 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4883 static CHANSERV_FUNC(cmd_inviteme
)
4885 if(GetUserMode(channel
, user
))
4887 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4890 if(channel
->channel_info
4891 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4893 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4896 irc_invite(cmd
->parent
->bot
, user
, channel
);
4901 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4904 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4906 /* We display things based on two dimensions:
4907 * - Issue time: present or absent
4908 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4909 * (in order of precedence, so something both expired and revoked
4910 * only counts as revoked)
4912 combo
= (suspended
->issued
? 4 : 0)
4913 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4915 case 0: /* no issue time, indefinite expiration */
4916 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4918 case 1: /* no issue time, expires in future */
4919 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4920 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4922 case 2: /* no issue time, expired */
4923 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4924 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4926 case 3: /* no issue time, revoked */
4927 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4928 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4930 case 4: /* issue time set, indefinite expiration */
4931 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4932 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4934 case 5: /* issue time set, expires in future */
4935 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4936 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4937 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4939 case 6: /* issue time set, expired */
4940 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4941 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4942 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4944 case 7: /* issue time set, revoked */
4945 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4946 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4947 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4950 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4956 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4959 const char *fmt
= "%a %b %d %H:%M %Y";
4960 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4962 if(giveownership
->staff_issuer
)
4964 if(giveownership
->reason
)
4965 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4966 giveownership
->target
, giveownership
->target_access
,
4967 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4969 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4970 giveownership
->target
, giveownership
->target_access
,
4971 giveownership
->staff_issuer
, buf
);
4975 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4980 static CHANSERV_FUNC(cmd_info
)
4982 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4983 struct userData
*uData
, *owner
;
4984 struct chanData
*cData
;
4985 struct do_not_register
*dnr
;
4990 cData
= channel
->channel_info
;
4991 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4992 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4995 uData
= GetChannelUser(cData
, user
->handle_info
);
4996 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4998 mod_chanmode_format(&cData
->modes
, modes
);
4999 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5000 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5003 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5007 note
= iter_data(it
);
5008 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5011 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5012 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5015 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5016 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5017 if(owner
->access
== UL_OWNER
)
5018 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5019 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5020 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5021 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5023 privileged
= IsStaff(user
);
5024 /* if(privileged) */
5025 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5026 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5027 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5029 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5030 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5032 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5034 struct suspended
*suspended
;
5035 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5036 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5037 show_suspension_info(cmd
, user
, suspended
);
5039 else if(IsSuspended(cData
))
5041 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5042 show_suspension_info(cmd
, user
, cData
->suspended
);
5044 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5046 struct giveownership
*giveownership
;
5047 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5048 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5049 show_giveownership_info(cmd
, user
, giveownership
);
5051 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5052 reply("CSMSG_CHANNEL_END");
5054 reply("CSMSG_CHANNEL_END_CLEAN");
5058 static CHANSERV_FUNC(cmd_netinfo
)
5060 extern time_t boot_time
;
5061 extern unsigned long burst_length
;
5062 char interval
[INTERVALLEN
];
5064 reply("CSMSG_NETWORK_INFO");
5065 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5066 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5067 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5068 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5069 reply("CSMSG_NETWORK_LAMERS", banCount
);
5070 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5071 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5072 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5077 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5079 struct helpfile_table table
;
5081 struct userNode
*user
;
5086 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5087 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5088 for(nn
=0; nn
<list
->used
; nn
++)
5090 user
= list
->list
[nn
];
5091 if(user
->modes
& skip_flags
)
5095 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5098 nick
= alloca(strlen(user
->nick
)+3);
5099 sprintf(nick
, "(%s)", user
->nick
);
5103 table
.contents
[table
.length
][0] = nick
;
5106 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5109 static CHANSERV_FUNC(cmd_ircops
)
5111 reply("CSMSG_STAFF_OPERS");
5112 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5116 static CHANSERV_FUNC(cmd_helpers
)
5118 reply("CSMSG_STAFF_HELPERS");
5119 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5123 static CHANSERV_FUNC(cmd_staff
)
5125 reply("CSMSG_NETWORK_STAFF");
5126 cmd_ircops(CSFUNC_ARGS
);
5127 cmd_helpers(CSFUNC_ARGS
);
5131 static CHANSERV_FUNC(cmd_peek
)
5133 struct modeNode
*mn
;
5134 char modes
[MODELEN
];
5136 struct helpfile_table table
;
5138 irc_make_chanmode(channel
, modes
);
5140 reply("CSMSG_PEEK_INFO", channel
->name
);
5141 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5143 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5144 reply("CSMSG_PEEK_MODES", modes
);
5145 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5149 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5150 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5151 for(n
= 0; n
< channel
->members
.used
; n
++)
5153 mn
= channel
->members
.list
[n
];
5154 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5156 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5157 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5162 reply("CSMSG_PEEK_OPS");
5163 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5166 reply("CSMSG_PEEK_NO_OPS");
5167 reply("CSMSG_PEEK_END");
5171 static MODCMD_FUNC(cmd_wipeinfo
)
5173 struct handle_info
*victim
;
5174 struct userData
*ud
, *actor
;
5177 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5178 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5180 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5182 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5185 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5187 reply("MSG_USER_OUTRANKED", victim
->handle
);
5193 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5198 resync_channel(struct chanNode
*channel
)
5200 struct mod_chanmode
*changes
;
5201 struct chanData
*cData
= channel
->channel_info
;
5202 unsigned int ii
, used
;
5204 /* 6 = worst case -ovh+ovh on everyone */
5205 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5206 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5208 struct modeNode
*mn
= channel
->members
.list
[ii
];
5209 struct userData
*uData
;
5211 if(IsService(mn
->user
))
5215 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5217 /* If the channel is in no-mode mode, de-mode EVERYONE */
5218 if(cData
->chOpts
[chAutomode
] == 'n')
5222 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5223 changes
->args
[used
++].u
.member
= mn
;
5226 else /* Give various userlevels their modes.. */
5228 if(uData
&& uData
->access
>= UL_OP
)
5230 if(!(mn
->modes
& MODE_CHANOP
))
5232 changes
->args
[used
].mode
= MODE_CHANOP
;
5233 changes
->args
[used
++].u
.member
= mn
;
5236 else if(uData
&& uData
->access
>= UL_HALFOP
)
5238 if(mn
->modes
& MODE_CHANOP
)
5240 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5241 changes
->args
[used
++].u
.member
= mn
;
5243 if(!(mn
->modes
& MODE_HALFOP
))
5245 changes
->args
[used
].mode
= MODE_HALFOP
;
5246 changes
->args
[used
++].u
.member
= mn
;
5249 else if(uData
&& uData
->access
>= UL_PEON
)
5251 if(mn
->modes
& MODE_CHANOP
)
5253 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5254 changes
->args
[used
++].u
.member
= mn
;
5256 if(mn
->modes
& MODE_HALFOP
)
5258 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5259 changes
->args
[used
++].u
.member
= mn
;
5261 /* Don't voice peons if were in mode m */
5262 if( cData
->chOpts
[chAutomode
] == 'm')
5264 if(mn
->modes
& MODE_VOICE
)
5266 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5267 changes
->args
[used
++].u
.member
= mn
;
5270 /* otherwise, make user they do have voice */
5271 else if(!(mn
->modes
& MODE_VOICE
))
5273 changes
->args
[used
].mode
= MODE_VOICE
;
5274 changes
->args
[used
++].u
.member
= mn
;
5277 else /* They arnt on the userlist.. */
5279 /* If we voice everyone, but they dont.. */
5280 if(cData
->chOpts
[chAutomode
] == 'v')
5282 /* Remove anything except v */
5283 if(mn
->modes
& ~MODE_VOICE
)
5285 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5286 changes
->args
[used
++].u
.member
= mn
;
5289 if(!(mn
->modes
& MODE_VOICE
))
5291 changes
->args
[used
].mode
= MODE_VOICE
;
5292 changes
->args
[used
++].u
.member
= mn
;
5295 /* If we hop everyone, but they dont.. */
5296 else if(cData
->chOpts
[chAutomode
] == 'h')
5298 /* Remove anything except h */
5299 if(mn
->modes
& ~MODE_HALFOP
)
5301 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5302 changes
->args
[used
++].u
.member
= mn
;
5305 if(!(mn
->modes
& MODE_HALFOP
))
5307 changes
->args
[used
].mode
= MODE_HALFOP
;
5308 changes
->args
[used
++].u
.member
= mn
;
5311 /* If we op everyone, but they dont.. */
5312 else if(cData
->chOpts
[chAutomode
] == 'o')
5314 /* Remove anything except h */
5315 if(mn
->modes
& ~MODE_CHANOP
)
5317 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5318 changes
->args
[used
++].u
.member
= mn
;
5321 if(!(mn
->modes
& MODE_CHANOP
))
5323 changes
->args
[used
].mode
= MODE_CHANOP
;
5324 changes
->args
[used
++].u
.member
= mn
;
5327 /* they have no excuse for having modes, de-everything them */
5332 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5333 changes
->args
[used
++].u
.member
= mn
;
5339 changes
->argc
= used
;
5340 mod_chanmode_announce(chanserv
, channel
, changes
);
5341 mod_chanmode_free(changes
);
5344 static CHANSERV_FUNC(cmd_resync
)
5346 resync_channel(channel
);
5347 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5351 static CHANSERV_FUNC(cmd_seen
)
5353 struct userData
*uData
;
5354 struct handle_info
*handle
;
5355 char seen
[INTERVALLEN
];
5359 if(!irccasecmp(argv
[1], chanserv
->nick
))
5361 reply("CSMSG_IS_CHANSERV");
5365 if(!(handle
= get_handle_info(argv
[1])))
5367 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5371 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5373 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5378 reply("CSMSG_USER_PRESENT", handle
->handle
);
5379 else if(uData
->seen
)
5380 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5382 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5384 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5385 reply("CSMSG_USER_VACATION", handle
->handle
);
5390 static MODCMD_FUNC(cmd_names
)
5392 struct userNode
*targ
;
5393 struct userData
*targData
;
5394 unsigned int ii
, pos
;
5397 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5399 targ
= channel
->members
.list
[ii
]->user
;
5400 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5403 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5406 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5410 if(IsUserSuspended(targData
))
5412 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5415 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5416 reply("CSMSG_END_NAMES", channel
->name
);
5421 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5423 switch(ntype
->visible_type
)
5425 case NOTE_VIS_ALL
: return 1;
5426 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5427 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5432 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5434 struct userData
*uData
;
5436 switch(ntype
->set_access_type
)
5438 case NOTE_SET_CHANNEL_ACCESS
:
5439 if(!user
->handle_info
)
5441 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5443 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5444 case NOTE_SET_CHANNEL_SETTER
:
5445 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5446 case NOTE_SET_PRIVILEGED
: default:
5447 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5451 static CHANSERV_FUNC(cmd_note
)
5453 struct chanData
*cData
;
5455 struct note_type
*ntype
;
5457 cData
= channel
->channel_info
;
5460 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5464 /* If no arguments, show all visible notes for the channel. */
5470 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5472 note
= iter_data(it
);
5473 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5476 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5477 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5480 reply("CSMSG_NOTELIST_END", channel
->name
);
5482 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5484 /* If one argument, show the named note. */
5487 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5488 && note_type_visible_to_user(cData
, note
->type
, user
))
5490 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5492 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5493 && note_type_visible_to_user(NULL
, ntype
, user
))
5495 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5500 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5504 /* Assume they're trying to set a note. */
5508 ntype
= dict_find(note_types
, argv
[1], NULL
);
5511 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5514 else if(note_type_settable_by_user(channel
, ntype
, user
))
5516 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5517 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5518 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5519 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5520 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5522 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5524 /* The note is viewable to staff only, so return 0
5525 to keep the invocation from getting logged (or
5526 regular users can see it in !events). */
5532 reply("CSMSG_NO_ACCESS");
5539 static CHANSERV_FUNC(cmd_delnote
)
5544 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5545 || !note_type_settable_by_user(channel
, note
->type
, user
))
5547 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5550 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5551 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5555 static CHANSERV_FUNC(cmd_last
)
5561 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5563 if(numoflines
< 1 || numoflines
> 200)
5565 reply("CSMSG_LAST_INVALID");
5568 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5572 static CHANSERV_FUNC(cmd_events
)
5574 struct logSearch discrim
;
5575 struct logReport report
;
5576 unsigned int matches
, limit
;
5578 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5579 if(limit
< 1 || limit
> 200)
5582 memset(&discrim
, 0, sizeof(discrim
));
5583 discrim
.masks
.bot
= chanserv
;
5584 discrim
.masks
.channel_name
= channel
->name
;
5586 discrim
.masks
.command
= argv
[2];
5587 discrim
.limit
= limit
;
5588 discrim
.max_time
= INT_MAX
;
5589 discrim
.severities
= 1 << LOG_COMMAND
;
5590 report
.reporter
= chanserv
;
5592 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5593 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5595 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5597 reply("MSG_MATCH_COUNT", matches
);
5599 reply("MSG_NO_MATCHES");
5603 static CHANSERV_FUNC(cmd_say
)
5609 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5610 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5612 else if(GetUserH(argv
[1]))
5615 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5616 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5620 reply("MSG_NOT_TARGET_NAME");
5626 static CHANSERV_FUNC(cmd_emote
)
5632 /* CTCP is so annoying. */
5633 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5634 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5636 else if(GetUserH(argv
[1]))
5638 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5639 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5643 reply("MSG_NOT_TARGET_NAME");
5649 struct channelList
*
5650 chanserv_support_channels(void)
5652 return &chanserv_conf
.support_channels
;
5655 static CHANSERV_FUNC(cmd_expire
)
5657 int channel_count
= registered_channels
;
5658 expire_channels(NULL
);
5659 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5664 chanserv_expire_suspension(void *data
)
5666 struct suspended
*suspended
= data
;
5667 struct chanNode
*channel
;
5669 if(!suspended
->expires
|| (now
< suspended
->expires
))
5670 suspended
->revoked
= now
;
5671 channel
= suspended
->cData
->channel
;
5672 suspended
->cData
->channel
= channel
;
5673 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5674 if(!IsOffChannel(suspended
->cData
))
5676 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5677 ss_cs_join_channel(channel
, 1);
5681 static CHANSERV_FUNC(cmd_csuspend
)
5683 struct suspended
*suspended
;
5684 char reason
[MAXLEN
];
5685 time_t expiry
, duration
;
5686 struct userData
*uData
;
5690 if(IsProtected(channel
->channel_info
))
5692 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5696 if(argv
[1][0] == '!')
5698 else if(IsSuspended(channel
->channel_info
))
5700 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5701 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5705 if(!strcmp(argv
[1], "0"))
5707 else if((duration
= ParseInterval(argv
[1])))
5708 expiry
= now
+ duration
;
5711 reply("MSG_INVALID_DURATION", argv
[1]);
5715 unsplit_string(argv
+ 2, argc
- 2, reason
);
5717 suspended
= calloc(1, sizeof(*suspended
));
5718 suspended
->revoked
= 0;
5719 suspended
->issued
= now
;
5720 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5721 suspended
->expires
= expiry
;
5722 suspended
->reason
= strdup(reason
);
5723 suspended
->cData
= channel
->channel_info
;
5724 suspended
->previous
= suspended
->cData
->suspended
;
5725 suspended
->cData
->suspended
= suspended
;
5727 if(suspended
->expires
)
5728 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5730 if(IsSuspended(channel
->channel_info
))
5732 suspended
->previous
->revoked
= now
;
5733 if(suspended
->previous
->expires
)
5734 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5736 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5737 channel
->name
, suspended
->suspender
);
5741 /* Mark all users in channel as absent. */
5742 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5751 /* Mark the channel as suspended, then part. */
5752 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5753 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5754 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5755 reply("CSMSG_SUSPENDED", channel
->name
);
5756 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5757 channel
->name
, suspended
->suspender
);
5762 static CHANSERV_FUNC(cmd_cunsuspend
)
5764 struct suspended
*suspended
;
5766 if(!IsSuspended(channel
->channel_info
))
5768 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5772 suspended
= channel
->channel_info
->suspended
;
5774 /* Expire the suspension and join ChanServ to the channel. */
5775 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5776 chanserv_expire_suspension(suspended
);
5777 reply("CSMSG_UNSUSPENDED", channel
->name
);
5778 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5779 channel
->name
, user
->handle_info
->handle
);
5783 typedef struct chanservSearch
5791 unsigned long flags
;
5795 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5798 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5803 search
= malloc(sizeof(struct chanservSearch
));
5804 memset(search
, 0, sizeof(*search
));
5807 for(i
= 0; i
< argc
; i
++)
5809 /* Assume all criteria require arguments. */
5812 reply("MSG_MISSING_PARAMS", argv
[i
]);
5816 if(!irccasecmp(argv
[i
], "name"))
5817 search
->name
= argv
[++i
];
5818 else if(!irccasecmp(argv
[i
], "registrar"))
5819 search
->registrar
= argv
[++i
];
5820 else if(!irccasecmp(argv
[i
], "unvisited"))
5821 search
->unvisited
= ParseInterval(argv
[++i
]);
5822 else if(!irccasecmp(argv
[i
], "registered"))
5823 search
->registered
= ParseInterval(argv
[++i
]);
5824 else if(!irccasecmp(argv
[i
], "flags"))
5827 if(!irccasecmp(argv
[i
], "nodelete"))
5828 search
->flags
|= CHANNEL_NODELETE
;
5829 else if(!irccasecmp(argv
[i
], "suspended"))
5830 search
->flags
|= CHANNEL_SUSPENDED
;
5833 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5837 else if(!irccasecmp(argv
[i
], "limit"))
5838 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5841 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5846 if(search
->name
&& !strcmp(search
->name
, "*"))
5848 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5849 search
->registrar
= 0;
5858 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5860 const char *name
= channel
->channel
->name
;
5861 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5862 (search
->registrar
&& !channel
->registrar
) ||
5863 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5864 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5865 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5866 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5873 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5875 struct chanData
*channel
;
5876 unsigned int matches
= 0;
5878 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5880 if(!chanserv_channel_match(channel
, search
))
5890 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5895 search_print(struct chanData
*channel
, void *data
)
5897 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5900 static CHANSERV_FUNC(cmd_search
)
5903 unsigned int matches
;
5904 channel_search_func action
;
5908 if(!irccasecmp(argv
[1], "count"))
5909 action
= search_count
;
5910 else if(!irccasecmp(argv
[1], "print"))
5911 action
= search_print
;
5914 reply("CSMSG_ACTION_INVALID", argv
[1]);
5918 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5922 if(action
== search_count
)
5923 search
->limit
= INT_MAX
;
5925 if(action
== search_print
)
5927 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5928 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5932 matches
= chanserv_channel_search(search
, action
, user
);
5935 reply("MSG_MATCH_COUNT", matches
);
5937 reply("MSG_NO_MATCHES");
5943 static CHANSERV_FUNC(cmd_unvisited
)
5945 struct chanData
*cData
;
5946 time_t interval
= chanserv_conf
.channel_expire_delay
;
5947 char buffer
[INTERVALLEN
];
5948 unsigned int limit
= 25, matches
= 0;
5952 interval
= ParseInterval(argv
[1]);
5954 limit
= atoi(argv
[2]);
5957 intervalString(buffer
, interval
, user
->handle_info
);
5958 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5960 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5962 if((now
- cData
->visited
) < interval
)
5965 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5966 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5973 static MODCMD_FUNC(chan_opt_defaulttopic
)
5979 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5981 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5985 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5987 free(channel
->channel_info
->topic
);
5988 if(topic
[0] == '*' && topic
[1] == 0)
5990 topic
= channel
->channel_info
->topic
= NULL
;
5994 topic
= channel
->channel_info
->topic
= strdup(topic
);
5995 if(channel
->channel_info
->topic_mask
5996 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5997 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5999 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6002 if(channel
->channel_info
->topic
)
6003 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6005 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6009 static MODCMD_FUNC(chan_opt_topicmask
)
6013 struct chanData
*cData
= channel
->channel_info
;
6016 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6018 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6022 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6024 if(cData
->topic_mask
)
6025 free(cData
->topic_mask
);
6026 if(mask
[0] == '*' && mask
[1] == 0)
6028 cData
->topic_mask
= 0;
6032 cData
->topic_mask
= strdup(mask
);
6034 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6035 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6036 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6040 if(channel
->channel_info
->topic_mask
)
6041 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6043 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6047 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6051 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6055 if(greeting
[0] == '*' && greeting
[1] == 0)
6059 unsigned int length
= strlen(greeting
);
6060 if(length
> chanserv_conf
.greeting_length
)
6062 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6065 *data
= strdup(greeting
);
6074 reply(name
, user_find_message(user
, "MSG_NONE"));
6078 static MODCMD_FUNC(chan_opt_greeting
)
6080 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6083 static MODCMD_FUNC(chan_opt_usergreeting
)
6085 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6088 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6090 unsigned int charmax
;
6093 charmax
= atoi(argv
[1]);
6094 if ((charmax
> 0) && (charmax
< chanserv_conf
.max_userinfo_length
))
6095 channel
->channel_info
->maxsetinfo
= charmax
;
6098 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6102 static MODCMD_FUNC(chan_opt_modes
)
6104 struct mod_chanmode
*new_modes
;
6105 char modes
[MODELEN
];
6109 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6110 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6114 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6116 reply("CSMSG_NO_ACCESS");
6119 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6121 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6123 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6125 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6128 else if(new_modes
->argc
> 1)
6130 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6131 mod_chanmode_free(new_modes
);
6136 channel
->channel_info
->modes
= *new_modes
;
6137 modcmd_chanmode_announce(new_modes
);
6138 mod_chanmode_free(new_modes
);
6142 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6144 reply("CSMSG_SET_MODES", modes
);
6146 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6150 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6152 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6154 struct chanData
*cData
= channel
->channel_info
;
6159 /* Set flag according to value. */
6160 if(enabled_string(argv
[1]))
6162 cData
->flags
|= mask
;
6165 else if(disabled_string(argv
[1]))
6167 cData
->flags
&= ~mask
;
6172 reply("MSG_INVALID_BINARY", argv
[1]);
6178 /* Find current option value. */
6179 value
= (cData
->flags
& mask
) ? 1 : 0;
6183 reply(name
, user_find_message(user
, "MSG_ON"));
6185 reply(name
, user_find_message(user
, "MSG_OFF"));
6189 static MODCMD_FUNC(chan_opt_nodelete
)
6191 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6193 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6197 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6200 static MODCMD_FUNC(chan_opt_dynlimit
)
6202 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6205 static MODCMD_FUNC(chan_opt_offchannel
)
6207 struct chanData
*cData
= channel
->channel_info
;
6212 /* Set flag according to value. */
6213 if(enabled_string(argv
[1]))
6215 if(!IsOffChannel(cData
))
6216 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6217 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6220 else if(disabled_string(argv
[1]))
6222 if(IsOffChannel(cData
))
6224 struct mod_chanmode change
;
6225 mod_chanmode_init(&change
);
6227 change
.args
[0].mode
= MODE_CHANOP
;
6228 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6229 mod_chanmode_announce(chanserv
, channel
, &change
);
6231 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6236 reply("MSG_INVALID_BINARY", argv
[1]);
6242 /* Find current option value. */
6243 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6247 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6249 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6253 static MODCMD_FUNC(chan_opt_defaults
)
6255 struct userData
*uData
;
6256 struct chanData
*cData
;
6257 const char *confirm
;
6258 enum levelOption lvlOpt
;
6259 enum charOption chOpt
;
6261 cData
= channel
->channel_info
;
6262 uData
= GetChannelUser(cData
, user
->handle_info
);
6263 if(!uData
|| (uData
->access
< UL_OWNER
))
6265 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6268 confirm
= make_confirmation_string(uData
);
6269 if((argc
< 2) || strcmp(argv
[1], confirm
))
6271 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6274 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6275 cData
->modes
= chanserv_conf
.default_modes
;
6276 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6277 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6278 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6279 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6280 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6285 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6287 struct chanData
*cData
= channel
->channel_info
;
6288 struct userData
*uData
;
6289 unsigned short value
;
6293 if(!check_user_level(channel
, user
, option
, 1, 1))
6295 reply("CSMSG_CANNOT_SET");
6298 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6299 if(!value
&& strcmp(argv
[1], "0"))
6301 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6304 uData
= GetChannelUser(cData
, user
->handle_info
);
6305 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6307 reply("CSMSG_BAD_SETLEVEL");
6313 /* This test only applies to owners, since non-owners
6314 * trying to set an option to above their level get caught
6315 * by the CSMSG_BAD_SETLEVEL test above.
6317 if(value
> uData
->access
)
6319 reply("CSMSG_BAD_SETTERS");
6326 cData
->lvlOpts
[option
] = value
;
6328 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6332 static MODCMD_FUNC(chan_opt_enfops
)
6334 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6337 static MODCMD_FUNC(chan_opt_enfhalfops
)
6339 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6341 static MODCMD_FUNC(chan_opt_enfmodes
)
6343 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6346 static MODCMD_FUNC(chan_opt_enftopic
)
6348 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6351 static MODCMD_FUNC(chan_opt_pubcmd
)
6353 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6356 static MODCMD_FUNC(chan_opt_setters
)
6358 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6361 static MODCMD_FUNC(chan_opt_userinfo
)
6363 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6366 static MODCMD_FUNC(chan_opt_topicsnarf
)
6368 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6371 static MODCMD_FUNC(chan_opt_inviteme
)
6373 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6376 /* TODO: Make look like this when no args are
6378 * -X3- -------------------------------
6379 * -X3- BanTimeout: Bans are removed:
6380 * -X3- ----- * indicates current -----
6381 * -X3- 0: [*] Never.
6382 * -X3- 1: [ ] After 10 minutes.
6383 * -X3- 2: [ ] After 2 hours.
6384 * -X3- 3: [ ] After 4 hours.
6385 * -X3- 4: [ ] After 24 hours.
6386 * -X3- 5: [ ] After one week.
6387 * -X3- ------------- End -------------
6390 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6392 struct chanData
*cData
= channel
->channel_info
;
6393 int count
= charOptions
[option
].count
, index
;
6397 index
= atoi(argv
[1]);
6399 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6401 reply("CSMSG_INVALID_NUMERIC", index
);
6402 /* Show possible values. */
6403 for(index
= 0; index
< count
; index
++)
6404 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6408 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6412 /* Find current option value. */
6415 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6419 /* Somehow, the option value is corrupt; reset it to the default. */
6420 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6425 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6429 static MODCMD_FUNC(chan_opt_automode
)
6431 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6434 static MODCMD_FUNC(chan_opt_protect
)
6436 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6439 static MODCMD_FUNC(chan_opt_toys
)
6441 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6444 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6446 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6449 static MODCMD_FUNC(chan_opt_bantimeout
)
6451 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6454 static MODCMD_FUNC(chan_opt_topicrefresh
)
6456 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6459 static MODCMD_FUNC(chan_opt_resync
)
6461 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6464 static struct svccmd_list set_shows_list
;
6467 handle_svccmd_unbind(struct svccmd
*target
) {
6469 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6470 if(target
== set_shows_list
.list
[ii
])
6471 set_shows_list
.used
= 0;
6474 static CHANSERV_FUNC(cmd_set
)
6476 struct svccmd
*subcmd
;
6480 /* Check if we need to (re-)initialize set_shows_list. */
6481 if(!set_shows_list
.used
)
6483 if(!set_shows_list
.size
)
6485 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6486 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6488 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6490 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6491 sprintf(buf
, "%s %s", argv
[0], name
);
6492 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6495 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6498 svccmd_list_append(&set_shows_list
, subcmd
);
6504 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6505 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6507 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6509 subcmd
= set_shows_list
.list
[ii
];
6510 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6512 reply("CSMSG_CHANNEL_OPTIONS_END");
6516 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6517 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6520 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6523 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6525 reply("CSMSG_NO_ACCESS");
6529 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6533 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6535 struct userData
*uData
;
6537 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6540 reply("CSMSG_NOT_USER", channel
->name
);
6546 /* Just show current option value. */
6548 else if(enabled_string(argv
[1]))
6550 uData
->flags
|= mask
;
6552 else if(disabled_string(argv
[1]))
6554 uData
->flags
&= ~mask
;
6558 reply("MSG_INVALID_BINARY", argv
[1]);
6562 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6566 static MODCMD_FUNC(user_opt_autoop
)
6568 struct userData
*uData
;
6570 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6573 reply("CSMSG_NOT_USER", channel
->name
);
6576 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6577 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6579 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6582 static MODCMD_FUNC(user_opt_autoinvite
)
6584 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6587 static MODCMD_FUNC(user_opt_autojoin
)
6589 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6592 static MODCMD_FUNC(user_opt_info
)
6594 struct userData
*uData
;
6597 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6601 /* If they got past the command restrictions (which require access)
6602 * but fail this test, we have some fool with security override on.
6604 reply("CSMSG_NOT_USER", channel
->name
);
6611 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6612 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
6614 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
6617 bp
= strcspn(infoline
, "\001");
6620 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6625 if(infoline
[0] == '*' && infoline
[1] == 0)
6628 uData
->info
= strdup(infoline
);
6631 reply("CSMSG_USET_INFO", uData
->info
);
6633 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6637 struct svccmd_list uset_shows_list
;
6639 static CHANSERV_FUNC(cmd_uset
)
6641 struct svccmd
*subcmd
;
6645 /* Check if we need to (re-)initialize uset_shows_list. */
6646 if(!uset_shows_list
.used
)
6650 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6653 if(!uset_shows_list
.size
)
6655 uset_shows_list
.size
= ArrayLength(options
);
6656 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6658 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6660 const char *name
= options
[ii
];
6661 sprintf(buf
, "%s %s", argv
[0], name
);
6662 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6665 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6668 svccmd_list_append(&uset_shows_list
, subcmd
);
6674 /* Do this so options are presented in a consistent order. */
6675 reply("CSMSG_USER_OPTIONS");
6676 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6677 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6681 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6682 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6685 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6689 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6692 static CHANSERV_FUNC(cmd_giveownership
)
6694 struct handle_info
*new_owner_hi
;
6695 struct userData
*new_owner
, *curr_user
;
6696 struct chanData
*cData
= channel
->channel_info
;
6697 struct do_not_register
*dnr
;
6698 struct giveownership
*giveownership
;
6699 unsigned int force
, override
;
6700 unsigned short co_access
, new_owner_old_access
;
6701 char transfer_reason
[MAXLEN
];
6704 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6705 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6707 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6708 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6709 && (uData
->access
> 500)
6710 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6711 || uData
->access
< 500));
6714 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6716 struct userData
*owner
= NULL
;
6717 for(curr_user
= channel
->channel_info
->users
;
6719 curr_user
= curr_user
->next
)
6721 if(curr_user
->access
!= UL_OWNER
)
6725 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6732 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6734 char delay
[INTERVALLEN
];
6735 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6736 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6740 reply("CSMSG_NO_OWNER", channel
->name
);
6743 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6745 if(new_owner_hi
== user
->handle_info
)
6747 reply("CSMSG_NO_TRANSFER_SELF");
6750 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6755 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
, 0);
6759 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6763 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6765 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6768 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6769 if(!IsHelping(user
))
6770 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6772 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6776 new_owner_old_access
= new_owner
->access
;
6777 if(new_owner
->access
>= UL_COOWNER
)
6778 co_access
= new_owner
->access
;
6780 co_access
= UL_COOWNER
;
6781 new_owner
->access
= UL_OWNER
;
6783 curr_user
->access
= co_access
;
6784 cData
->ownerTransfer
= now
;
6786 giveownership
= calloc(1, sizeof(*giveownership
));
6787 giveownership
->issued
= now
;
6788 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
6789 giveownership
->target
= strdup(new_owner_hi
->handle
);
6790 giveownership
->target_access
= new_owner_old_access
;
6793 if(argc
> (2 + force
))
6795 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6796 giveownership
->reason
= strdup(transfer_reason
);
6798 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6801 giveownership
->previous
= channel
->channel_info
->giveownership
;
6802 channel
->channel_info
->giveownership
= giveownership
;
6804 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6805 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6806 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6811 chanserv_expire_user_suspension(void *data
)
6813 struct userData
*target
= data
;
6815 target
->expires
= 0;
6816 target
->flags
&= ~USER_SUSPENDED
;
6819 static CHANSERV_FUNC(cmd_suspend
)
6821 struct handle_info
*hi
;
6822 struct userData
*self
, *target
;
6826 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6827 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6828 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6830 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6833 if(target
->access
>= self
->access
)
6835 reply("MSG_USER_OUTRANKED", hi
->handle
);
6838 if(target
->flags
& USER_SUSPENDED
)
6840 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6845 target
->present
= 0;
6848 if(!strcmp(argv
[2], "0"))
6852 unsigned int duration
;
6853 if(!(duration
= ParseInterval(argv
[2])))
6855 reply("MSG_INVALID_DURATION", argv
[2]);
6858 expiry
= now
+ duration
;
6861 target
->expires
= expiry
;
6864 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6866 target
->flags
|= USER_SUSPENDED
;
6867 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6871 static CHANSERV_FUNC(cmd_unsuspend
)
6873 struct handle_info
*hi
;
6874 struct userData
*self
, *target
;
6877 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6878 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6879 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6881 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6884 if(target
->access
>= self
->access
)
6886 reply("MSG_USER_OUTRANKED", hi
->handle
);
6889 if(!(target
->flags
& USER_SUSPENDED
))
6891 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6894 target
->flags
&= ~USER_SUSPENDED
;
6895 scan_user_presence(target
, NULL
);
6896 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6897 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6901 static MODCMD_FUNC(cmd_deleteme
)
6903 struct handle_info
*hi
;
6904 struct userData
*target
;
6905 const char *confirm_string
;
6906 unsigned short access
;
6909 hi
= user
->handle_info
;
6910 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6912 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6915 if(target
->access
== UL_OWNER
)
6917 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6920 confirm_string
= make_confirmation_string(target
);
6921 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6923 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6926 access
= target
->access
;
6927 channel_name
= strdup(channel
->name
);
6928 del_channel_user(target
, 1);
6929 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6935 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6937 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6938 struct chanData
*cData
;
6941 for(cData
= channelList
; cData
; cData
= cData
->next
)
6943 if(IsSuspended(cData
))
6945 opt
= cData
->chOpts
[chTopicRefresh
];
6948 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6951 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6952 cData
->last_refresh
= refresh_num
;
6954 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6958 chanserv_auto_resync(UNUSED_ARG(void *data
))
6960 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6961 struct chanData
*cData
;
6964 for(cData
= channelList
; cData
; cData
= cData
->next
)
6966 if(IsSuspended(cData
)) continue;
6967 opt
= cData
->chOpts
[chResync
];
6968 if(opt
== 'n') continue;
6969 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6970 resync_channel(cData
->channel
);
6971 cData
->last_resync
= refresh_num
;
6973 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6976 static CHANSERV_FUNC(cmd_unf
)
6980 char response
[MAXLEN
];
6981 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6982 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6983 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6986 reply("CSMSG_UNF_RESPONSE");
6990 static CHANSERV_FUNC(cmd_ping
)
6994 char response
[MAXLEN
];
6995 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6996 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6997 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7000 reply("CSMSG_PING_RESPONSE");
7004 static CHANSERV_FUNC(cmd_wut
)
7008 char response
[MAXLEN
];
7009 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7010 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7011 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7014 reply("CSMSG_WUT_RESPONSE");
7018 static CHANSERV_FUNC(cmd_roulette
)
7021 struct chanData
*cData
= channel
->channel_info
;
7024 if (cData
->roulette_chamber
) {
7025 DelUser(user
, chanserv
, 1, "BANG - Don't stuff bullets into a loaded gun");
7029 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_ROULETTE_LOADS");
7030 cData
->roulette_chamber
= 1 + rand() % 6;
7036 static CHANSERV_FUNC(cmd_shoot
)
7039 struct chanData
*cData
= channel
->channel_info
;
7041 if (cData
->roulette_chamber
<= 0) {
7042 reply("CSMSG_ROULETTE_NEW");
7046 cData
->roulette_chamber
--;
7048 if (cData
->roulette_chamber
== 0) {
7049 reply("CSMSG_ROULETTE_BANG");
7050 reply("CSMSG_ROULETTE_BETTER_LUCK", user
->nick
);
7051 DelUser(user
, chanserv
, 1, "BANG!!!!");
7053 reply("CSMSG_ROULETTE_CLICK");
7060 chanserv_remove_abuse(void *data
)
7062 char *remnick
= data
;
7063 struct userNode
*user
;
7064 /* sometimes the clone was killed and maybe even the user took their nick back
7065 * (ie, an oper) so dont kill them here after all unless they are local. */
7066 if( (user
= GetUserH(remnick
)) )
7068 DelUser(user
, NULL
, 1, "");
7071 int lamepart(struct userNode
*nick
) {
7072 struct modeNode
*mn
;
7073 unsigned int count
, n
;
7075 for (n
=count
=0; n
<nick
->channels
.used
; n
++) {
7076 mn
= nick
->channels
.list
[n
];
7077 irc_svspart(chanserv
, nick
, mn
->channel
);
7083 static CHANSERV_FUNC(cmd_spin
)
7088 int type
, lamep
= 1;
7091 tstr
= conf_get_data("server/type", RECDB_QSTRING
);
7099 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL1", user
->nick
);
7100 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL2");
7101 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_WHEEL3");
7103 if(chanserv_conf
.wheel
->used
< 1) {
7104 /* wheel actions not defined! eek */
7108 const char *wheel
= chanserv_conf
.wheel
->list
[ (int) ( (chanserv_conf
.wheel
->used
) * (rand() / (RAND_MAX
+ 1.0)) ) ];
7109 if(!wheel
&& *wheel
)
7112 /* enable this to be able to manually specify a result for testing:
7113 log_module(MAIN_LOG, LOG_DEBUG,"Testing wheel randomness: %s\n", wheel);
7119 /* connection reset by peer */
7120 if (!strcasecmp(wheel
, "peer")) {
7121 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PEER");
7123 irc_kill(chanserv
, user
, "Connection reset by peer");
7125 irc_svsquit(chanserv
, user
, "Connection reset by peer");
7127 /* part all channels */
7128 else if (!strcasecmp(wheel
, "partall")) {
7129 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_PARTALL");
7133 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7135 /* random time gline */
7136 else if (!strcasecmp(wheel
, "gline")) {
7137 char target
[IRC_NTOP_MAX_SIZE
+ 3];
7138 int wtime
= 120 + rand() % 600;
7140 strcpy(target
, "*@");
7141 strcat(target
, user
->hostname
);
7142 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_GLINE");
7144 gline_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1, 0);
7145 // irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7148 else if (!strcasecmp(wheel
, "shun")) {
7149 char target
[IRC_NTOP_MAX_SIZE
+ 3];
7150 int wtime
= 120 + rand() % 600;
7152 strcpy(target
, "*@");
7153 strcat(target
, user
->hostname
);
7154 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SHUN");
7156 shun_add(chanserv
->nick
, target
, wtime
, "Reward for spinning the wheel of misfortune!", now
, 1);
7158 /* absolutely nothing */
7159 else if (!strcasecmp(wheel
, "nothing")) {
7160 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NOTHING");
7162 /* join random chans and part em several times */
7163 else if (!strcasecmp(wheel
, "randjoin")) {
7169 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_RANDJOIN");
7170 while(complete
!= 1) {
7171 if (rndchans
!= 15) {
7172 chango
= 120 + rand() % 600;
7173 sputsock("%s SJ %s #%d %ld", self
->numeric
, user
->numeric
, chango
, now
);
7176 if (roundz0r
!= 1) {
7180 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7187 sputsock("%s SJ %s 0 "FMT_TIME_T
, self
->numeric
, user
->numeric
, now
);
7193 /* abuse line added to /whois */
7194 else if (!strcasecmp(wheel
, "abusewhois")) {
7195 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_ABUSEWHOIS");
7196 irc_swhois(chanserv
, user
, "is being defecated on by services");
7198 /* kick from each channel your in */
7199 else if (!strcasecmp(wheel
, "kickall")) {
7200 unsigned int count
, n
;
7201 struct modeNode
*mn
;
7203 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKALL");
7205 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7206 mn
= user
->channels
.list
[n
];
7207 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7210 /* random nick change */
7211 else if (!strcasecmp(wheel
, "nickchange")) {
7212 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_NICKCHANGE");
7214 char *oldnick
= NULL
;
7215 char *oldident
= NULL
;
7216 char *oldhost
= NULL
;
7217 char abusednick
[NICKLEN
] = "";
7218 int abusednum
= 1 + (int) (10000.0 * (rand() / (RAND_MAX
+ 1.0)));
7219 struct userNode
*clone
;
7221 oldnick
= strdup(user
->nick
);
7222 oldident
= strdup(user
->ident
);
7223 oldhost
= strdup(user
->hostname
);
7225 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7227 snprintf(abusednick
, NICKLEN
, "Abused%d", abusednum
+(1 + rand() % 120));
7228 log_module(MAIN_LOG
, LOG_DEBUG
, "Abused Nick: %s, Client Nick: %s", abusednick
, user
->nick
);
7229 if(!GetUserH(abusednick
))
7233 SVSNickChange(user
, abusednick
);
7234 irc_svsnick(chanserv
, user
, abusednick
);
7236 clone
= AddClone(oldnick
, oldident
, oldhost
, "I got abused by the wheel of misfortune :D");
7237 timeq_add(now
+ 300, chanserv_remove_abuse
, clone
->nick
);
7240 else if (!strcasecmp(wheel
, "kill")) {
7241 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KILL");
7243 DelUser(user
, chanserv
, 1, "Reward for spinning the wheel of misfortune!");
7244 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7246 /* service ignore */
7247 else if (!strcasecmp(wheel
, "svsignore")) {
7248 int gagged
, ignoretime
= 0;
7249 char target
[IRC_NTOP_MAX_SIZE
+ 13];
7252 /* we cant gag opers, so just verbally abuse them */
7253 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE_OPER");
7256 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_SVSIGNORE");
7258 strcpy(target
, "*!*@");
7259 strcat(target
, user
->hostname
);
7260 ignoretime
= now
+ (1 + rand() % 120);
7262 gagged
= gag_create(target
, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime
);
7264 /* kick and ban from each channel your in */
7265 else if (!strcasecmp(wheel
, "kickbanall")) {
7266 unsigned int count
, n
;
7267 struct modeNode
*mn
;
7268 //char ban[IRC_NTOP_MAX_SIZE + 1];
7270 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_KICKBANALL");
7272 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7273 for (n
=count
=0; n
<user
->channels
.used
; n
++) {
7274 struct mod_chanmode
*change
;
7275 struct banData
*bData
;
7276 unsigned int exists
;
7280 ban
= generate_hostmask(user
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
|GENMASK_USENICK
);
7282 log_module(MAIN_LOG
, LOG_DEBUG
, "Generated ban %s", ban
);
7283 mn
= user
->channels
.list
[n
];
7284 if(mn
->channel
->banlist
.used
>= MAXBANS
) {
7285 reply("CSMSG_BANLIST_FULL", mn
->channel
->name
);
7290 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7292 change
= mod_chanmode_alloc(1);
7293 change
->args
[0].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
7294 change
->args
[0].u
.member
= GetUserMode(mn
->channel
, user
);
7297 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7298 mod_chanmode_free(change
);
7300 exists
= ChannelBanExists(mn
->channel
, ban
);
7302 change
= mod_chanmode_alloc(1);
7303 change
->args
[0].mode
= MODE_BAN
;
7304 change
->args
[0].u
.hostmask
= ban
;
7306 mod_chanmode_announce(chanserv
, mn
->channel
, change
);
7307 mod_chanmode_free(change
);
7311 reply("CSMSG_REDUNDANT_BAN", ban
, mn
->channel
->name
);
7315 irc_kick(chanserv
, user
, mn
->channel
, "Reward for spinning the wheel of misfortune!");
7319 send_target_message(1, channel
->name
, chanserv
, "CSMSG_SPIN_UNKNOWN", wheel
);
7326 static CHANSERV_FUNC(cmd_8ball
)
7328 unsigned int i
, j
, accum
;
7333 for(i
=1; i
<argc
; i
++)
7334 for(j
=0; argv
[i
][j
]; j
++)
7335 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7336 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7339 char response
[MAXLEN
];
7340 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7341 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7344 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7348 #else /* Use cool 8ball instead */
7350 void eightball(char *outcome
, int method
, unsigned int seed
)
7354 #define NUMOFCOLORS 18
7355 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7356 "white", "black", "grey", "brown",
7357 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7358 "fuchsia","turquoise","magenta", "cyan"};
7359 #define NUMOFLOCATIONS 50
7360 char balllocations
[50][55] = {
7361 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7362 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7363 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7364 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7365 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7366 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7367 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7368 "your bra", "your hair", "your bed", "the couch", "the wall",
7369 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7370 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7371 #define NUMOFPREPS 15
7372 char ballpreps
[50][50] = {
7373 "Near", "Somewhere near", "In", "In", "In",
7374 "In", "Hiding in", "Under", "Next to", "Over",
7375 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7376 #define NUMOFNUMS 34
7377 char ballnums
[50][50] = {
7378 "A hundred", "A thousand", "A few", "42",
7379 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7380 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7381 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7383 #define NUMOFMULTS 8
7384 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7387 * 0: normal (Not used in x3)
7394 if (method
== 1) /* A Color */
7398 answer
= (rand() % 12); /* Make sure this is the # of entries */
7401 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
7403 case 1: strcpy(tmp
, "Sort of a light %s color.");
7405 case 2: strcpy(tmp
, "Dark and dreary %s.");
7407 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
7409 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
7411 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
7413 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
7415 case 10: strcpy(tmp
, "Solid %s.");
7417 case 11: strcpy(tmp
, "Transparent %s.");
7419 default: strcpy(outcome
, "An invalid random number was generated.");
7422 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
7425 else if (method
== 2) /* Location */
7427 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
7429 else if (method
== 3) /* Number of ___ */
7431 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
7435 //Debug(DBGWARNING, "Error in 8ball.");
7440 static CHANSERV_FUNC(cmd_8ball
)
7442 char *word1
, *word2
, *word3
;
7443 static char eb
[MAXLEN
];
7444 unsigned int accum
, i
, j
;
7448 for(i
=1; i
<argc
; i
++)
7449 for(j
=0; argv
[i
][j
]; j
++)
7450 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7452 accum
+= time(NULL
)/3600;
7454 word2
= argc
>2?argv
[2]:"";
7455 word3
= argc
>3?argv
[3]:"";
7458 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
7459 eightball(eb
, 1, accum
);
7460 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7461 eightball(eb
, 1, accum
);
7462 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7463 eightball(eb
, 1, accum
);
7464 /*** LOCATION *****/
7469 (strcasecmp(word1
, "where") == 0) &&
7470 (strcasecmp(word2
, "is") == 0)
7474 strcasecmp(word1
, "where's") == 0
7477 eightball(eb
, 2, accum
);
7479 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
7480 eightball(eb
, 3, accum
);
7484 /* Generic 8ball question.. so pull from x3.conf srvx style */
7487 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7490 char response
[MAXLEN
];
7491 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
7492 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7495 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7501 char response
[MAXLEN
];
7502 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
7503 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7506 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
7511 static CHANSERV_FUNC(cmd_d
)
7513 unsigned long sides
, count
, modifier
, ii
, total
;
7514 char response
[MAXLEN
], *sep
;
7518 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
7528 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
7529 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
7533 else if((sep
[0] == '-') && isdigit(sep
[1]))
7534 modifier
= strtoul(sep
, NULL
, 10);
7535 else if((sep
[0] == '+') && isdigit(sep
[1]))
7536 modifier
= strtoul(sep
+1, NULL
, 10);
7543 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
7548 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7551 for(total
= ii
= 0; ii
< count
; ++ii
)
7552 total
+= (rand() % sides
) + 1;
7555 if((count
> 1) || modifier
)
7557 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7558 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7562 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7563 sprintf(response
, fmt
, total
, sides
);
7566 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7568 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7572 static CHANSERV_FUNC(cmd_huggle
)
7574 /* CTCP must be via PRIVMSG, never notice */
7576 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7578 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7582 static CHANSERV_FUNC(cmd_calc
)
7584 char response
[MAXLEN
];
7587 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7590 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7592 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7596 static CHANSERV_FUNC(cmd_reply
)
7600 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7603 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7605 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7610 chanserv_adjust_limit(void *data
)
7612 struct mod_chanmode change
;
7613 struct chanData
*cData
= data
;
7614 struct chanNode
*channel
= cData
->channel
;
7617 if(IsSuspended(cData
))
7620 cData
->limitAdjusted
= now
;
7621 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7622 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7624 if(limit
> cData
->modes
.new_limit
)
7625 limit
= cData
->modes
.new_limit
;
7626 else if(limit
== cData
->modes
.new_limit
)
7630 mod_chanmode_init(&change
);
7631 change
.modes_set
= MODE_LIMIT
;
7632 change
.new_limit
= limit
;
7633 mod_chanmode_announce(chanserv
, channel
, &change
);
7637 handle_new_channel(struct chanNode
*channel
)
7639 struct chanData
*cData
;
7641 if(!(cData
= channel
->channel_info
))
7644 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7645 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7647 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7648 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7652 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
7654 struct banData
*bData
;
7655 struct mod_chanmode
*change
;
7657 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
7662 if (chan
->channel_info
) {
7663 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
7665 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
))
7677 check_bans(struct userNode
*user
, const char *channel
)
7679 struct chanNode
*chan
;
7680 struct mod_chanmode change
;
7681 struct chanData
*cData
;
7682 struct banData
*bData
;
7684 if (!(chan
= GetChannel(channel
)))
7687 if(!(cData
= chan
->channel_info
))
7690 mod_chanmode_init(&change
);
7693 if(chan
->banlist
.used
< MAXBANS
)
7695 /* Not joining through a ban. */
7696 for(bData
= cData
->bans
;
7697 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7698 bData
= bData
->next
);
7702 char kick_reason
[MAXLEN
];
7703 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7705 bData
->triggered
= now
;
7706 if(bData
!= cData
->bans
)
7708 /* Shuffle the ban to the head of the list. */
7710 bData
->next
->prev
= bData
->prev
;
7712 bData
->prev
->next
= bData
->next
;
7715 bData
->next
= cData
->bans
;
7718 cData
->bans
->prev
= bData
;
7720 cData
->bans
= bData
;
7723 change
.args
[0].mode
= MODE_BAN
;
7724 change
.args
[0].u
.hostmask
= bData
->mask
;
7725 mod_chanmode_announce(chanserv
, chan
, &change
);
7726 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
7734 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
7737 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
7739 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
))
7746 /* Welcome to my worst nightmare. Warning: Read (or modify)
7747 the code below at your own risk. */
7749 handle_join(struct modeNode
*mNode
)
7751 struct mod_chanmode change
;
7752 struct userNode
*user
= mNode
->user
;
7753 struct chanNode
*channel
= mNode
->channel
;
7754 struct chanData
*cData
;
7755 struct userData
*uData
= NULL
;
7756 struct banData
*bData
;
7757 struct handle_info
*handle
;
7758 unsigned int modes
= 0, info
= 0;
7761 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7764 cData
= channel
->channel_info
;
7765 if(channel
->members
.used
> cData
->max
)
7766 cData
->max
= channel
->members
.used
;
7769 /* Check for bans. If they're joining through a ban, one of two
7771 * 1: Join during a netburst, by riding the break. Kick them
7772 * unless they have ops or voice in the channel.
7773 * 2: They're allowed to join through the ban (an invite in
7774 * ircu2.10, or a +e on Hybrid, or something).
7775 * If they're not joining through a ban, and the banlist is not
7776 * full, see if they're on the banlist for the channel. If so,
7779 if(user
->uplink
->burst
&& !mNode
->modes
)
7782 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7784 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7786 /* Riding a netburst. Naughty. */
7787 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7794 if(user
->handle_info
)
7796 handle
= user
->handle_info
;
7799 uData
= GetTrueChannelAccess(cData
, handle
);
7804 mod_chanmode_init(&change
);
7807 /* TODO: maybe only people above inviteme level? -Rubin */
7808 /* We don't kick people with access */
7809 if(!uData
&& !channel_user_is_exempt(user
, channel
))
7811 if(channel
->banlist
.used
< MAXBANS
)
7813 /* Not joining through a ban. */
7814 for(bData
= cData
->bans
;
7815 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7816 bData
= bData
->next
);
7820 char kick_reason
[MAXLEN
];
7821 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7823 bData
->triggered
= now
;
7824 if(bData
!= cData
->bans
)
7826 /* Shuffle the ban to the head of the list. */
7828 bData
->next
->prev
= bData
->prev
;
7830 bData
->prev
->next
= bData
->next
;
7833 bData
->next
= cData
->bans
;
7836 cData
->bans
->prev
= bData
;
7837 cData
->bans
= bData
;
7840 change
.args
[0].mode
= MODE_BAN
;
7841 change
.args
[0].u
.hostmask
= bData
->mask
;
7842 mod_chanmode_announce(chanserv
, channel
, &change
);
7843 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7849 /* ChanServ will not modify the limits in join-flooded channels.
7850 It will also skip DynLimit processing when the user (or srvx)
7851 is bursting in, because there are likely more incoming. */
7852 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7853 && !user
->uplink
->burst
7854 && !channel
->join_flooded
7855 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7857 /* The user count has begun "bumping" into the channel limit,
7858 so set a timer to raise the limit a bit. Any previous
7859 timers are removed so three incoming users within the delay
7860 results in one limit change, not three. */
7862 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7863 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7866 /* Give automodes exept during join-floods */
7867 if(!channel
->join_flooded
)
7869 if(cData
->chOpts
[chAutomode
] == 'v')
7870 modes
|= MODE_VOICE
;
7871 else if(cData
->chOpts
[chAutomode
] == 'h')
7872 modes
|= MODE_HALFOP
;
7873 else if(cData
->chOpts
[chAutomode
] == 'o')
7874 modes
|= MODE_CHANOP
;
7877 greeting
= cData
->greeting
;
7878 if(user
->handle_info
)
7880 /* handle = user->handle_info; */
7882 if(IsHelper(user
) && !IsHelping(user
))
7885 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7887 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7889 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7895 /* uData = GetTrueChannelAccess(cData, handle); */
7896 if(uData
&& !IsUserSuspended(uData
))
7898 /* non users getting automodes are handled above. */
7899 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7901 /* just op everyone with access */
7902 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7903 modes
|= MODE_VOICE
;
7904 /* or do their access level */
7905 else if(uData
->access
>= UL_OP
)
7906 modes
|= MODE_CHANOP
;
7907 else if(uData
->access
>= UL_HALFOP
)
7908 modes
|= MODE_HALFOP
;
7909 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7910 modes
|= MODE_VOICE
;
7912 if(uData
->access
>= UL_PRESENT
)
7913 cData
->visited
= now
;
7914 if(cData
->user_greeting
)
7915 greeting
= cData
->user_greeting
;
7917 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7918 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7926 /* If user joining normally (not during burst), apply op or voice,
7927 * and send greeting/userinfo as appropriate.
7929 if(!user
->uplink
->burst
)
7933 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7934 if(modes & MODE_CHANOP) {
7935 modes &= ~MODE_HALFOP;
7936 modes &= ~MODE_VOICE;
7939 change
.args
[0].mode
= modes
;
7940 change
.args
[0].u
.member
= mNode
;
7941 mod_chanmode_announce(chanserv
, channel
, &change
);
7944 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7946 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7952 chanserv_autojoin_channels(struct userNode
*user
)
7954 struct userData
*channel
;
7956 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7958 struct chanNode
*cn
;
7959 struct modeNode
*mn
;
7961 if(IsUserSuspended(channel
)
7962 || IsSuspended(channel
->channel
)
7963 || !(cn
= channel
->channel
->channel
))
7966 mn
= GetUserMode(cn
, user
);
7969 if(!IsUserSuspended(channel
)
7970 && IsUserAutoJoin(channel
)
7971 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7973 && !user
->uplink
->burst
)
7974 irc_svsjoin(chanserv
, user
, cn
);
7980 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7982 struct mod_chanmode change
;
7983 struct userData
*channel
;
7984 unsigned int ii
, jj
, i
;
7986 if(!user
->handle_info
)
7989 mod_chanmode_init(&change
);
7991 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7993 struct chanNode
*cn
;
7994 struct chanData
*cData
;
7995 struct modeNode
*mn
;
7996 if(IsUserSuspended(channel
)
7997 || IsSuspended(channel
->channel
)
7998 || !(cn
= channel
->channel
->channel
))
8001 cData
= cn
->channel_info
;
8002 mn
= GetUserMode(cn
, user
);
8005 if(!IsUserSuspended(channel
)
8006 && IsUserAutoInvite(channel
)
8007 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8009 && !user
->uplink
->burst
)
8010 irc_invite(chanserv
, user
, cn
);
8014 if(channel
->access
>= UL_PRESENT
)
8015 channel
->channel
->visited
= now
;
8017 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8019 if(channel
->access
>= UL_OP
)
8020 change
.args
[0].mode
= MODE_CHANOP
;
8021 else if(channel
->access
>= UL_HALFOP
)
8022 change
.args
[0].mode
= MODE_HALFOP
;
8023 else if(channel
->access
>= UL_PEON
)
8024 change
.args
[0].mode
= MODE_VOICE
;
8026 change
.args
[0].mode
= 0;
8027 change
.args
[0].u
.member
= mn
;
8028 if(change
.args
[0].mode
)
8029 mod_chanmode_announce(chanserv
, cn
, &change
);
8032 channel
->seen
= now
;
8033 channel
->present
= 1;
8036 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8038 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
8039 struct banData
*ban
;
8041 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8042 || !channel
->channel_info
8043 || IsSuspended(channel
->channel_info
))
8045 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8047 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8048 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8050 if(jj
< channel
->banlist
.used
)
8052 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
8054 char kick_reason
[MAXLEN
];
8055 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
8057 change
.args
[0].mode
= MODE_BAN
;
8058 change
.args
[0].u
.hostmask
= ban
->mask
;
8059 mod_chanmode_announce(chanserv
, channel
, &change
);
8060 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8061 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8062 ban
->triggered
= now
;
8067 if(IsSupportHelper(user
))
8069 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8071 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8073 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8079 if (user
->handle_info
->ignores
->used
) {
8080 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8081 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8085 if (user
->handle_info
->epithet
)
8086 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8088 /* process autojoin channels 5 seconds later as this sometimes
8089 happens before autohide */
8090 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8091 chanserv_autojoin_channels(user
);
8095 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
8097 struct chanData
*cData
;
8098 struct userData
*uData
;
8100 cData
= mn
->channel
->channel_info
;
8101 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8104 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8106 /* Allow for a bit of padding so that the limit doesn't
8107 track the user count exactly, which could get annoying. */
8108 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8110 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8111 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8115 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8117 scan_user_presence(uData
, mn
->user
);
8121 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8123 unsigned int ii
, jj
;
8124 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8126 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
8127 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
8129 if(jj
< mn
->user
->channels
.used
)
8132 if(ii
== chanserv_conf
.support_channels
.used
)
8133 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8138 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
8140 struct userData
*uData
;
8142 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8143 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8144 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8147 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8149 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8150 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8153 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8158 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
8160 struct chanData
*cData
;
8162 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8165 cData
= channel
->channel_info
;
8166 if(bad_topic(channel
, user
, channel
->topic
))
8167 { /* User doesnt have privs to set topics. Undo it */
8168 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8169 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8172 /* If there is a topic mask set, and the new topic doesnt match,
8173 * set the topic to mask + new_topic */
8174 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8176 char new_topic
[TOPICLEN
+1];
8177 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8180 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8181 /* and fall through to topicsnarf code below.. */
8183 else /* Topic couldnt fit into mask, was too long */
8185 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8186 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8187 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8191 /* With topicsnarf, grab the topic and save it as the default topic. */
8192 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8195 cData
->topic
= strdup(channel
->topic
);
8201 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
8203 struct mod_chanmode
*bounce
= NULL
;
8204 unsigned int bnc
, ii
;
8207 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8210 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8211 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8213 char correct
[MAXLEN
];
8214 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8215 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8216 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8218 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8220 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8222 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8223 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8226 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8229 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8230 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8231 if(bounce
->args
[bnc
].u
.member
)
8235 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8236 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8238 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8240 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8242 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8243 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8246 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8247 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8248 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8251 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8253 const char *ban
= change
->args
[ii
].u
.hostmask
;
8254 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8257 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8258 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8259 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8261 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8266 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8267 mod_chanmode_announce(chanserv
, channel
, bounce
);
8268 for(ii
= 0; ii
< change
->argc
; ++ii
)
8269 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8270 free((char*)bounce
->args
[ii
].u
.hostmask
);
8271 mod_chanmode_free(bounce
);
8276 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
8278 struct chanNode
*channel
;
8279 struct banData
*bData
;
8280 struct mod_chanmode change
;
8281 unsigned int ii
, jj
;
8282 char kick_reason
[MAXLEN
];
8284 mod_chanmode_init(&change
);
8286 change
.args
[0].mode
= MODE_BAN
;
8287 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8289 channel
= user
->channels
.list
[ii
]->channel
;
8290 /* Need not check for bans if they're opped or voiced. */
8291 /* TODO: does this make sense in automode v, h, and o? *
8292 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8293 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8295 /* Need not check for bans unless channel registration is active. */
8296 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8298 /* Look for a matching ban already on the channel. */
8299 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8300 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8302 /* Need not act if we found one. */
8303 if(jj
< channel
->banlist
.used
)
8305 /* don't kick someone on the userlist */
8306 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8308 /* Look for a matching ban in this channel. */
8309 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8311 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
8313 change
.args
[0].u
.hostmask
= bData
->mask
;
8314 mod_chanmode_announce(chanserv
, channel
, &change
);
8315 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8316 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8317 bData
->triggered
= now
;
8318 break; /* we don't need to check any more bans in the channel */
8323 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
8325 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8329 dict_remove2(handle_dnrs
, old_handle
, 1);
8330 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8331 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8336 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
8338 struct userNode
*h_user
;
8340 if(handle
->channels
)
8342 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8343 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8345 while(handle
->channels
)
8346 del_channel_user(handle
->channels
, 1);
8351 handle_server_link(UNUSED_ARG(struct server
*server
))
8353 struct chanData
*cData
;
8355 for(cData
= channelList
; cData
; cData
= cData
->next
)
8357 if(!IsSuspended(cData
))
8358 cData
->may_opchan
= 1;
8359 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8360 && !cData
->channel
->join_flooded
8361 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8362 < chanserv_conf
.adjust_threshold
))
8364 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8365 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8371 chanserv_conf_read(void)
8375 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
8376 struct mod_chanmode
*change
;
8377 struct string_list
*strlist
;
8378 struct chanNode
*chan
;
8381 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
8383 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
8386 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8387 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8388 chanserv_conf
.support_channels
.used
= 0;
8389 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
8391 for(ii
= 0; ii
< strlist
->used
; ++ii
)
8393 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8396 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
8398 channelList_append(&chanserv_conf
.support_channels
, chan
);
8401 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
8404 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8407 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
8409 channelList_append(&chanserv_conf
.support_channels
, chan
);
8411 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
8412 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
8413 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
8414 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
8415 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
8416 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
8417 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
8418 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
8419 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
8420 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
8421 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
8422 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
8423 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
8424 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
8425 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
8426 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
8427 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
8428 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
8429 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
8430 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
8431 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
8432 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
8433 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
8434 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
8435 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
8437 NickChange(chanserv
, str
, 0);
8438 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
8439 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
8440 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
8441 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
8442 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
8443 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
8444 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
8445 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
8446 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
8447 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
8448 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
8449 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
8450 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
8451 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
8452 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
8453 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
8454 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
8455 god_timeout
= str
? ParseInterval(str
) : 60*15;
8456 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
8459 safestrncpy(mode_line
, str
, sizeof(mode_line
));
8460 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
8461 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
8462 && (change
->argc
< 2))
8464 chanserv_conf
.default_modes
= *change
;
8465 mod_chanmode_free(change
);
8467 free_string_list(chanserv_conf
.wheel
);
8468 strlist
= database_get_data(conf_node
, "wheel", RECDB_STRING_LIST
);
8470 strlist
= string_list_copy(strlist
);
8473 static const char *list
[] = {
8474 "peer", "partall", "gline", /* "shun", */
8475 "nothing", "randjoin", "abusewhois", "kickall",
8476 "nickchange", "kill", "svsignore", "kickbanall",
8479 strlist
= alloc_string_list(ArrayLength(list
)-1);
8480 for(ii
=0; list
[ii
]; ii
++)
8481 string_list_append(strlist
, strdup(list
[ii
]));
8483 chanserv_conf
.wheel
= strlist
;
8485 free_string_list(chanserv_conf
.set_shows
);
8486 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
8488 strlist
= string_list_copy(strlist
);
8491 static const char *list
[] = {
8492 /* free form text */
8493 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8494 /* options based on user level */
8495 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8496 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
8497 /* multiple choice options */
8498 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
8499 /* binary options */
8500 "DynLimit", "NoDelete", "BanTimeout",
8505 strlist
= alloc_string_list(ArrayLength(list
)-1);
8506 for(ii
=0; list
[ii
]; ii
++)
8507 string_list_append(strlist
, strdup(list
[ii
]));
8509 chanserv_conf
.set_shows
= strlist
;
8510 /* We don't look things up now, in case the list refers to options
8511 * defined by modules initialized after this point. Just mark the
8512 * function list as invalid, so it will be initialized.
8514 set_shows_list
.used
= 0;
8516 free_string_list(chanserv_conf
.eightball
);
8517 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
8520 strlist
= string_list_copy(strlist
);
8524 strlist
= alloc_string_list(4);
8525 string_list_append(strlist
, strdup("Yes."));
8526 string_list_append(strlist
, strdup("No."));
8527 string_list_append(strlist
, strdup("Maybe so."));
8529 chanserv_conf
.eightball
= strlist
;
8531 free_string_list(chanserv_conf
.old_ban_names
);
8532 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
8534 strlist
= string_list_copy(strlist
);
8536 strlist
= alloc_string_list(2);
8537 chanserv_conf
.old_ban_names
= strlist
;
8538 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
8539 off_channel
= str
? atoi(str
) : 0;
8543 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
8546 struct note_type
*ntype
;
8549 if(!(obj
= GET_RECORD_OBJECT(rd
)))
8551 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
8554 if(!(ntype
= chanserv_create_note_type(key
)))
8556 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
8560 /* Figure out set access */
8561 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
8563 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8564 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
8566 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
8568 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
8569 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
8571 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
8573 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
8577 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
8578 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8579 ntype
->set_access
.min_opserv
= 0;
8582 /* Figure out visibility */
8583 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
8584 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8585 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
8586 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8587 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
8588 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
8589 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
8590 ntype
->visible_type
= NOTE_VIS_ALL
;
8592 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8594 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
8595 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
8599 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8601 struct handle_info
*handle
;
8602 struct userData
*uData
;
8603 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
8605 unsigned short access
, lastaccess
= 0;
8607 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8609 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
8613 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
8614 if(access
> UL_OWNER
)
8616 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
8620 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
8621 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
8622 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
8623 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
8624 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8625 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
8626 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
8627 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
8628 lastaccess
= lstacc
? atoi(lstacc
) : 0;
8630 handle
= get_handle_info(key
);
8633 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
8637 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
, 0);
8638 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
8639 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
8641 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
8642 if (uData
->accessexpiry
> 0)
8643 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
8645 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
8646 if (uData
->clvlexpiry
> 0)
8647 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
8649 uData
->lastaccess
= lastaccess
;
8651 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
8653 if(uData
->expires
> now
)
8654 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
8656 uData
->flags
&= ~USER_SUSPENDED
;
8659 /* Upgrade: set autoop to the inverse of noautoop */
8660 if(chanserv_read_version
< 2)
8662 /* if noautoop is true, set autoop false, and vice versa */
8663 if(uData
->flags
& USER_NOAUTO_OP
)
8664 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
8666 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
8667 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
);
8673 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8675 struct banData
*bData
;
8676 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
8677 time_t set_time
, triggered_time
, expires_time
;
8679 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8681 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8685 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8686 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8687 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8688 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8689 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8690 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8691 if (!reason
|| !owner
)
8694 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8695 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8697 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8699 expires_time
= set_time
+ atoi(s_duration
);
8703 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8706 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8709 static struct suspended
*
8710 chanserv_read_suspended(dict_t obj
)
8712 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8716 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8717 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8718 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8719 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8720 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8721 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8722 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8723 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8724 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8725 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8729 static struct giveownership
*
8730 chanserv_read_giveownership(dict_t obj
)
8732 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8736 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8737 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8739 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8741 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8742 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8744 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8745 giveownership
->reason
= str
? strdup(str
) : NULL
;
8746 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8747 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8749 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8750 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8751 return giveownership
;
8755 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8757 struct suspended
*suspended
;
8758 struct giveownership
*giveownership
;
8759 struct mod_chanmode
*modes
;
8760 struct chanNode
*cNode
;
8761 struct chanData
*cData
;
8762 struct dict
*channel
, *obj
;
8763 char *str
, *argv
[10];
8767 channel
= hir
->d
.object
;
8769 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8772 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8775 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8778 cData
= register_channel(cNode
, str
);
8781 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8785 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8787 enum levelOption lvlOpt
;
8788 enum charOption chOpt
;
8790 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8791 cData
->flags
= atoi(str
);
8793 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8795 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8797 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8798 else if(levelOptions
[lvlOpt
].old_flag
)
8800 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8801 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8803 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8807 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8809 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8811 cData
->chOpts
[chOpt
] = str
[0];
8814 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8816 enum levelOption lvlOpt
;
8817 enum charOption chOpt
;
8820 cData
->flags
= base64toint(str
, 5);
8821 count
= strlen(str
+= 5);
8822 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8825 if(levelOptions
[lvlOpt
].old_flag
)
8827 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8828 lvl
= levelOptions
[lvlOpt
].flag_value
;
8830 lvl
= levelOptions
[lvlOpt
].default_value
;
8832 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8834 case 'c': lvl
= UL_COOWNER
; break;
8835 case 'm': lvl
= UL_MANAGER
; break;
8836 case 'n': lvl
= UL_OWNER
+1; break;
8837 case 'o': lvl
= UL_OP
; break;
8838 case 'p': lvl
= UL_PEON
; break;
8839 case 'h': lvl
= UL_HALFOP
; break;
8840 case 'w': lvl
= UL_OWNER
; break;
8841 default: lvl
= 0; break;
8843 cData
->lvlOpts
[lvlOpt
] = lvl
;
8845 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8846 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8849 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8851 suspended
= chanserv_read_suspended(obj
);
8852 cData
->suspended
= suspended
;
8853 suspended
->cData
= cData
;
8854 /* We could use suspended->expires and suspended->revoked to
8855 * set the CHANNEL_SUSPENDED flag, but we don't. */
8857 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8859 suspended
= calloc(1, sizeof(*suspended
));
8860 suspended
->issued
= 0;
8861 suspended
->revoked
= 0;
8862 suspended
->suspender
= strdup(str
);
8863 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8864 suspended
->expires
= str
? atoi(str
) : 0;
8865 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8866 suspended
->reason
= strdup(str
? str
: "No reason");
8867 suspended
->previous
= NULL
;
8868 cData
->suspended
= suspended
;
8869 suspended
->cData
= cData
;
8873 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8874 suspended
= NULL
; /* to squelch a warning */
8877 if(IsSuspended(cData
)) {
8878 if(suspended
->expires
> now
)
8879 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8880 else if(suspended
->expires
)
8881 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8884 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8886 giveownership
= chanserv_read_giveownership(obj
);
8887 cData
->giveownership
= giveownership
;
8890 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8891 struct mod_chanmode change
;
8892 mod_chanmode_init(&change
);
8894 change
.args
[0].mode
= MODE_CHANOP
;
8895 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8896 mod_chanmode_announce(chanserv
, cNode
, &change
);
8899 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8900 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8901 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8902 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8903 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8904 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8905 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8906 cData
->max
= str
? atoi(str
) : 0;
8907 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8908 cData
->greeting
= str
? strdup(str
) : NULL
;
8909 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8910 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8911 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8912 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8913 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8914 cData
->topic
= str
? strdup(str
) : NULL
;
8916 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
8917 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
8919 if(!IsSuspended(cData
)
8920 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8921 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8922 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8923 cData
->modes
= *modes
;
8925 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8926 if(cData
->modes
.argc
> 1)
8927 cData
->modes
.argc
= 1;
8928 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8929 mod_chanmode_free(modes
);
8932 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8933 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8934 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8936 if(!cData
->users
&& !IsProtected(cData
))
8938 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8939 unregister_channel(cData
, "has empty user list.");
8943 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8944 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8945 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8947 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8948 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8950 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8951 struct record_data
*rd
= iter_data(it
);
8952 const char *note
, *setter
;
8954 if(rd
->type
!= RECDB_OBJECT
)
8956 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8960 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8962 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8964 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8968 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8969 if(!setter
) setter
= "<unknown>";
8970 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8978 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8980 const char *setter
, *reason
, *str
;
8981 struct do_not_register
*dnr
;
8983 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8986 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8989 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8992 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8995 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8998 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9000 dnr
->set
= atoi(str
);
9006 chanserv_version_read(struct dict
*section
)
9010 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9012 chanserv_read_version
= atoi(str
);
9013 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9017 chanserv_saxdb_read(struct dict
*database
)
9019 struct dict
*section
;
9022 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9023 chanserv_version_read(section
);
9025 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9026 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9027 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9029 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9030 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9031 chanserv_channel_read(iter_key(it
), iter_data(it
));
9033 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9034 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9035 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9041 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9043 int high_present
= 0;
9044 saxdb_start_record(ctx
, KEY_USERS
, 1);
9045 for(; uData
; uData
= uData
->next
)
9047 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9049 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9050 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9051 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9052 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9053 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9054 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9056 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9058 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9060 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9061 saxdb_end_record(ctx
);
9063 saxdb_end_record(ctx
);
9064 return high_present
;
9068 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9072 saxdb_start_record(ctx
, KEY_BANS
, 1);
9073 for(; bData
; bData
= bData
->next
)
9075 saxdb_start_record(ctx
, bData
->mask
, 0);
9076 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9077 if(bData
->triggered
)
9078 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9080 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9082 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9084 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9085 saxdb_end_record(ctx
);
9087 saxdb_end_record(ctx
);
9091 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9093 saxdb_start_record(ctx
, name
, 0);
9094 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9095 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9097 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9099 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9101 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9103 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9104 saxdb_end_record(ctx
);
9108 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9110 saxdb_start_record(ctx
, name
, 0);
9111 if(giveownership
->staff_issuer
)
9112 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9113 if(giveownership
->old_owner
)
9114 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9115 if(giveownership
->target
)
9116 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9117 if(giveownership
->target_access
)
9118 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9119 if(giveownership
->reason
)
9120 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9121 if(giveownership
->issued
)
9122 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9123 if(giveownership
->previous
)
9124 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9125 saxdb_end_record(ctx
);
9129 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9133 enum levelOption lvlOpt
;
9134 enum charOption chOpt
;
9136 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9138 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9139 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9141 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9142 if(channel
->registrar
)
9143 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9144 if(channel
->greeting
)
9145 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9146 if(channel
->user_greeting
)
9147 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9148 if(channel
->topic_mask
)
9149 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9150 if(channel
->suspended
)
9151 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9152 if(channel
->giveownership
)
9153 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9155 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9156 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9157 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9158 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9159 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9161 buf
[0] = channel
->chOpts
[chOpt
];
9163 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9165 saxdb_end_record(ctx
);
9167 if (channel
->maxsetinfo
)
9168 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
9170 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9172 mod_chanmode_format(&channel
->modes
, buf
);
9173 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9176 high_present
= chanserv_write_users(ctx
, channel
->users
);
9177 chanserv_write_bans(ctx
, channel
->bans
);
9179 if(dict_size(channel
->notes
))
9183 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9184 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9186 struct note
*note
= iter_data(it
);
9187 saxdb_start_record(ctx
, iter_key(it
), 0);
9188 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9189 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9190 saxdb_end_record(ctx
);
9192 saxdb_end_record(ctx
);
9195 if(channel
->ownerTransfer
)
9196 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9197 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9198 saxdb_end_record(ctx
);
9202 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9206 saxdb_start_record(ctx
, ntype
->name
, 0);
9207 switch(ntype
->set_access_type
)
9209 case NOTE_SET_CHANNEL_ACCESS
:
9210 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9212 case NOTE_SET_CHANNEL_SETTER
:
9213 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9215 case NOTE_SET_PRIVILEGED
: default:
9216 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9219 switch(ntype
->visible_type
)
9221 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9222 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9223 case NOTE_VIS_PRIVILEGED
:
9224 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9226 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9227 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9228 saxdb_end_record(ctx
);
9232 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9234 struct do_not_register
*dnr
;
9237 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
9239 dnr
= iter_data(it
);
9240 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9242 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9243 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9244 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9245 saxdb_end_record(ctx
);
9250 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9253 struct chanData
*channel
;
9255 /* Version Control*/
9256 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9257 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9258 saxdb_end_record(ctx
);
9261 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9262 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9263 chanserv_write_note_type(ctx
, iter_data(it
));
9264 saxdb_end_record(ctx
);
9267 saxdb_start_record(ctx
, KEY_DNR
, 1);
9268 write_dnrs_helper(ctx
, handle_dnrs
);
9269 write_dnrs_helper(ctx
, plain_dnrs
);
9270 write_dnrs_helper(ctx
, mask_dnrs
);
9271 saxdb_end_record(ctx
);
9274 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9275 for(channel
= channelList
; channel
; channel
= channel
->next
)
9276 chanserv_write_channel(ctx
, channel
);
9277 saxdb_end_record(ctx
);
9283 chanserv_db_cleanup(void) {
9285 unreg_part_func(handle_part
);
9287 unregister_channel(channelList
, "terminating.");
9288 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9289 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9290 free(chanserv_conf
.support_channels
.list
);
9291 dict_delete(handle_dnrs
);
9292 dict_delete(plain_dnrs
);
9293 dict_delete(mask_dnrs
);
9294 dict_delete(note_types
);
9295 free_string_list(chanserv_conf
.eightball
);
9296 free_string_list(chanserv_conf
.old_ban_names
);
9297 free_string_list(chanserv_conf
.wheel
);
9298 free_string_list(chanserv_conf
.set_shows
);
9299 free(set_shows_list
.list
);
9300 free(uset_shows_list
.list
);
9303 struct userData
*helper
= helperList
;
9304 helperList
= helperList
->next
;
9309 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
9310 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9311 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9314 init_chanserv(const char *nick
)
9316 struct chanNode
*chan
;
9319 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9320 conf_register_reload(chanserv_conf_read
);
9322 reg_server_link_func(handle_server_link
);
9324 reg_new_channel_func(handle_new_channel
);
9325 reg_join_func(handle_join
);
9326 reg_part_func(handle_part
);
9327 reg_kick_func(handle_kick
);
9328 reg_topic_func(handle_topic
);
9329 reg_mode_change_func(handle_mode
);
9330 reg_nick_change_func(handle_nick_change
);
9332 reg_auth_func(handle_auth
);
9333 reg_handle_rename_func(handle_rename
);
9334 reg_unreg_func(handle_unreg
);
9336 handle_dnrs
= dict_new();
9337 dict_set_free_data(handle_dnrs
, free
);
9338 plain_dnrs
= dict_new();
9339 dict_set_free_data(plain_dnrs
, free
);
9340 mask_dnrs
= dict_new();
9341 dict_set_free_data(mask_dnrs
, free
);
9343 reg_svccmd_unbind_func(handle_svccmd_unbind
);
9344 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
9345 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
9346 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9347 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
9348 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
9349 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9350 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9351 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
9352 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
9354 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9356 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
9357 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
9359 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9360 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9361 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9362 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9363 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9365 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
9366 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
9367 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
9368 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9369 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9370 DEFINE_COMMAND(mdelpal
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9371 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9373 DEFINE_COMMAND(levels
, 1, 0, NULL
);
9375 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9376 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
9377 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9378 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
9380 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9381 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
9382 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9383 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9384 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
9385 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
9386 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9387 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9388 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9389 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9391 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9392 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9393 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9394 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
9395 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
9396 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
9397 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
9398 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
9399 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
9400 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
9401 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
9402 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
9403 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9404 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9406 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9407 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9408 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9409 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9411 /* if you change dellamer access, see also places
9412 * like unbanme which have manager hardcoded. */
9413 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9414 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
9416 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
9418 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
9420 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9421 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9422 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9423 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9424 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9425 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9426 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9427 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9428 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9429 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9430 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9431 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9433 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
9434 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
9436 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
9437 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
9438 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
9439 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
9441 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9442 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9443 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
9444 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
9445 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
9447 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9448 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9449 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9450 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9451 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9452 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9453 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9454 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9455 DEFINE_COMMAND(roulette
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9456 DEFINE_COMMAND(shoot
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9457 DEFINE_COMMAND(spin
, 1, MODCMD_REQUIRE_AUTHED
, "spin", "+nolog,+toy,+acceptchan", NULL
);
9459 /* Channel options */
9460 DEFINE_CHANNEL_OPTION(defaulttopic
);
9461 DEFINE_CHANNEL_OPTION(topicmask
);
9462 DEFINE_CHANNEL_OPTION(greeting
);
9463 DEFINE_CHANNEL_OPTION(usergreeting
);
9464 DEFINE_CHANNEL_OPTION(modes
);
9465 DEFINE_CHANNEL_OPTION(enfops
);
9466 DEFINE_CHANNEL_OPTION(enfhalfops
);
9467 DEFINE_CHANNEL_OPTION(automode
);
9468 DEFINE_CHANNEL_OPTION(protect
);
9469 DEFINE_CHANNEL_OPTION(enfmodes
);
9470 DEFINE_CHANNEL_OPTION(enftopic
);
9471 DEFINE_CHANNEL_OPTION(pubcmd
);
9472 DEFINE_CHANNEL_OPTION(userinfo
);
9473 DEFINE_CHANNEL_OPTION(dynlimit
);
9474 DEFINE_CHANNEL_OPTION(topicsnarf
);
9475 DEFINE_CHANNEL_OPTION(nodelete
);
9476 DEFINE_CHANNEL_OPTION(toys
);
9477 DEFINE_CHANNEL_OPTION(setters
);
9478 DEFINE_CHANNEL_OPTION(topicrefresh
);
9479 DEFINE_CHANNEL_OPTION(resync
);
9480 DEFINE_CHANNEL_OPTION(ctcpreaction
);
9481 DEFINE_CHANNEL_OPTION(bantimeout
);
9482 DEFINE_CHANNEL_OPTION(inviteme
);
9483 DEFINE_CHANNEL_OPTION(maxsetinfo
);
9485 DEFINE_CHANNEL_OPTION(offchannel
);
9486 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
9488 /* Alias set topic to set defaulttopic for compatibility. */
9489 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
9492 DEFINE_USER_OPTION(autoinvite
);
9493 DEFINE_USER_OPTION(autojoin
);
9494 DEFINE_USER_OPTION(info
);
9495 DEFINE_USER_OPTION(autoop
);
9497 /* Alias uset autovoice to uset autoop. */
9498 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
9500 note_types
= dict_new();
9501 dict_set_free_data(note_types
, chanserv_deref_note_type
);
9504 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
9505 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
9506 service_register(chanserv
)->trigger
= '!';
9507 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
9510 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
9512 if(chanserv_conf
.channel_expire_frequency
)
9513 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
9515 if(chanserv_conf
.ban_timeout_frequency
)
9516 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
9518 if(chanserv_conf
.refresh_period
)
9520 time_t next_refresh
;
9521 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
9522 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
9523 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
9526 if (autojoin_channels
&& chanserv
) {
9527 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
9528 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
9529 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
9533 reg_exit_func(chanserv_db_cleanup
);
9534 message_register_table(msgtab
);