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.
25 #include "opserv.h" /* for opserv_bad_channel() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
43 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
61 /* ChanServ database */
62 #define KEY_VERSION_CONTROL "version_control"
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* version control paramiter */
67 #define KEY_VERSION_NUMBER "version_number"
69 /* Note type parameters */
70 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
71 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
72 #define KEY_NOTE_SETTER_ACCESS "setter_access"
73 #define KEY_NOTE_VISIBILITY "visibility"
74 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
75 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
76 #define KEY_NOTE_VIS_ALL "all"
77 #define KEY_NOTE_MAX_LENGTH "max_length"
78 #define KEY_NOTE_SETTER "setter"
79 #define KEY_NOTE_NOTE "note"
81 /* Do-not-register channels */
83 #define KEY_DNR_SET "set"
84 #define KEY_DNR_SETTER "setter"
85 #define KEY_DNR_REASON "reason"
88 #define KEY_REGISTERED "registered"
89 #define KEY_REGISTRAR "registrar"
90 #define KEY_SUSPENDED "suspended"
91 #define KEY_PREVIOUS "previous"
92 #define KEY_SUSPENDER "suspender"
93 #define KEY_ISSUED "issued"
94 #define KEY_REVOKED "revoked"
95 #define KEY_SUSPEND_EXPIRES "suspend_expires"
96 #define KEY_SUSPEND_REASON "suspend_reason"
97 #define KEY_GIVEOWNERSHIP "giveownership"
98 #define KEY_STAFF_ISSUER "staff_issuer"
99 #define KEY_OLD_OWNER "old_owner"
100 #define KEY_TARGET "target"
101 #define KEY_TARGET_ACCESS "target_access"
102 #define KEY_VISITED "visited"
103 #define KEY_TOPIC "topic"
104 #define KEY_GREETING "greeting"
105 #define KEY_USER_GREETING "user_greeting"
106 #define KEY_MODES "modes"
107 #define KEY_FLAGS "flags"
108 #define KEY_OPTIONS "options"
109 #define KEY_USERS "users"
110 #define KEY_BANS "bans" /* for lamers */
111 #define KEY_MAX "max"
112 #define KEY_NOTES "notes"
113 #define KEY_TOPIC_MASK "topic_mask"
114 #define KEY_OWNER_TRANSFER "owner_transfer"
117 #define KEY_LEVEL "level"
118 #define KEY_INFO "info"
119 #define KEY_SEEN "seen"
120 #define KEY_ACCESSEXPIRY "accessexpiry"
121 #define KEY_CLVLEXPIRY "clvlexpiry"
122 #define KEY_LASTLEVEL "lastlevel"
125 #define KEY_OWNER "owner"
126 #define KEY_REASON "reason"
127 #define KEY_SET "set"
128 #define KEY_DURATION "duration"
129 #define KEY_EXPIRES "expires"
130 #define KEY_TRIGGERED "triggered"
132 #define KEY_GOD_TIMEOUT "god_timeout"
134 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
135 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
136 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
138 /* Administrative messages */
139 static const struct message_entry msgtab
[] = {
140 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
142 /* Channel registration */
143 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
144 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
145 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
146 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
147 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
148 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
149 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
150 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
152 /* Do-not-register channels */
153 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
154 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
155 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
156 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
157 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
158 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
159 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
160 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
161 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
162 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
163 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
164 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
165 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
166 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
168 /* Channel unregistration */
169 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
170 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
171 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
172 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
175 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
176 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
178 /* Channel merging */
179 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
180 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
181 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
182 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
183 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
185 /* Handle unregistration */
186 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
189 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
190 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
191 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
192 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
193 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
194 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
195 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
196 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
197 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
198 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
199 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
200 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
201 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
202 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
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_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
235 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
236 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
239 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
240 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
241 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
242 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
243 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
244 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
245 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
246 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
247 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
248 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
249 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
250 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
251 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
252 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
253 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
254 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
255 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
257 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
259 /* Channel management */
260 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
261 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
262 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
264 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
265 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
266 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
267 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
268 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
269 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
270 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
272 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
273 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
274 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
275 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
276 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
277 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
278 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
279 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
280 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
281 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
282 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
283 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
284 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
285 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
286 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
287 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
288 { "CSMSG_SET_MODES", "$bModes $b %s" },
289 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
290 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
291 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
292 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
293 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
294 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
295 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
296 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
297 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
298 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
299 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
300 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
301 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
302 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
303 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
304 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
305 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
306 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
307 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
308 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
310 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
311 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
312 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
313 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
314 { "CSMSG_USET_INFO", "$bInfo $b %s" },
316 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
317 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
318 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
319 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
320 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
321 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
322 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
323 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
324 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
325 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
326 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
328 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
329 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
330 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
331 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
332 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
333 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
334 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
336 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
337 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
338 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
339 { "CSMSG_PROTECT_NONE", "No users will be protected." },
340 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
341 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
342 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
344 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
345 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
346 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
347 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
348 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
350 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
351 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
352 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
353 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
354 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
356 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
357 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
358 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
359 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
360 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
362 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
363 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
364 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
365 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
366 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
367 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
369 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
370 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
371 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
372 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
373 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
374 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
375 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
376 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
377 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
379 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
380 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
381 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
383 /* Channel userlist */
384 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
385 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
386 /* uncomment if needed to adujust styles (and change code below)
387 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
388 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
389 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
390 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
391 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
392 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
394 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
395 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
396 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
398 /* Channel note list */
399 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
400 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
401 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
402 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
403 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
404 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
405 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
406 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
407 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
408 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
409 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
410 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
411 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
412 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
413 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
415 /* Channel [un]suspension */
416 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
417 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
418 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
419 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
420 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
421 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
422 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
424 /* Access information */
425 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
426 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
427 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
428 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
429 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
430 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
431 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
432 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
433 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
434 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
435 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
436 { "CSMSG_UC_H_TITLE", "network helper" },
437 { "CSMSG_LC_H_TITLE", "support helper" },
438 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
440 /* Seen information */
441 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
442 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
443 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
444 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
446 /* Names information */
447 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
448 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
450 /* Channel information */
451 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
452 { "CSMSG_BAR", "----------------------------------------"},
453 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
454 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
455 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
456 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
457 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
458 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
459 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
460 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
461 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
462 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
463 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
464 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
465 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
466 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
467 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
468 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
469 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
470 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
471 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
472 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
473 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
474 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
475 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
476 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
477 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
478 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
480 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
481 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
482 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
483 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
484 { "CSMSG_PEEK_OPS", "$bOps:$b" },
485 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
486 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
488 /* Network information */
489 { "CSMSG_NETWORK_INFO", "Network Information:" },
490 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
491 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
492 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
493 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
494 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
495 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
496 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
497 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
500 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
501 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
502 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
504 /* Channel searches */
505 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
506 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
507 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
508 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
510 /* Channel configuration */
511 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
512 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
513 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
514 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
515 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
518 { "CSMSG_USER_OPTIONS", "User Options:" },
519 // { "CSMSG_USER_PROTECTED", "That user is protected." },
522 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
523 { "CSMSG_PING_RESPONSE", "Pong!" },
524 { "CSMSG_WUT_RESPONSE", "wut" },
525 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
526 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
527 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
528 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
529 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
530 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
531 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
534 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
535 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
536 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
537 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
541 /* eject_user and unban_user flags */
542 #define ACTION_KICK 0x0001
543 #define ACTION_BAN 0x0002
544 #define ACTION_ADD_LAMER 0x0004
545 #define ACTION_ADD_TIMED_LAMER 0x0008
546 #define ACTION_UNBAN 0x0010
547 #define ACTION_DEL_LAMER 0x0020
549 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
550 #define MODELEN 40 + KEYLEN
554 #define CSFUNC_ARGS user, channel, argc, argv, cmd
556 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
557 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
558 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
559 reply("MSG_MISSING_PARAMS", argv[0]); \
563 DECLARE_LIST(dnrList
, struct do_not_register
*);
564 DEFINE_LIST(dnrList
, struct do_not_register
*);
566 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
568 struct userNode
*chanserv
;
571 extern struct string_list
*autojoin_channels
;
572 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
573 static struct log_type
*CS_LOG
;
574 struct adduserPending
* adduser_pendings
= NULL
;
575 unsigned int adduser_pendings_count
= 0;
576 unsigned long god_timeout
;
580 struct channelList support_channels
;
581 struct mod_chanmode default_modes
;
583 unsigned long db_backup_frequency
;
584 unsigned long channel_expire_frequency
;
585 unsigned long dnr_expire_frequency
;
586 unsigned long ban_timeout_frequency
;
589 unsigned int adjust_delay
;
590 long channel_expire_delay
;
591 unsigned int nodelete_level
;
593 unsigned int adjust_threshold
;
594 int join_flood_threshold
;
596 unsigned int greeting_length
;
597 unsigned int refresh_period
;
598 unsigned int giveownership_period
;
600 unsigned int max_owned
;
601 unsigned int max_chan_users
;
602 unsigned int max_chan_bans
; /* lamers */
603 unsigned int max_userinfo_length
;
605 struct string_list
*set_shows
;
606 struct string_list
*eightball
;
607 struct string_list
*old_ban_names
;
609 const char *ctcp_short_ban_duration
;
610 const char *ctcp_long_ban_duration
;
612 const char *irc_operator_epithet
;
613 const char *network_helper_epithet
;
614 const char *support_helper_epithet
;
619 struct userNode
*user
;
620 struct userNode
*bot
;
621 struct chanNode
*channel
;
623 unsigned short lowest
;
624 unsigned short highest
;
625 struct userData
**users
;
626 struct helpfile_table table
;
629 enum note_access_type
631 NOTE_SET_CHANNEL_ACCESS
,
632 NOTE_SET_CHANNEL_SETTER
,
636 enum note_visible_type
639 NOTE_VIS_CHANNEL_USERS
,
645 enum note_access_type set_access_type
;
647 unsigned int min_opserv
;
648 unsigned short min_ulevel
;
650 enum note_visible_type visible_type
;
651 unsigned int max_length
;
658 struct note_type
*type
;
659 char setter
[NICKSERV_HANDLE_LEN
+1];
663 static unsigned int registered_channels
;
664 static unsigned int banCount
;
666 static const struct {
669 unsigned short level
;
671 } accessLevels
[] = { /* MUST be orderd less to most! */
672 { "peon", "Peon", UL_PEON
, '+' },
673 { "halfop", "HalfOp", UL_HALFOP
, '%' },
674 { "op", "Op", UL_OP
, '@' },
675 { "manager", "Manager", UL_MANAGER
, '%' },
676 { "coowner", "Coowner", UL_COOWNER
, '*' },
677 { "owner", "Owner", UL_OWNER
, '!' },
678 { "helper", "BUG:", UL_HELPER
, 'X' }
681 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
682 static const struct {
685 unsigned short default_value
;
686 unsigned int old_idx
;
687 unsigned int old_flag
;
688 unsigned short flag_value
;
690 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
691 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
692 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
693 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
694 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
695 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
696 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
697 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
698 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
701 struct charOptionValues
{
704 } automodeValues
[] = {
705 { 'n', "CSMSG_AUTOMODE_NONE" },
706 { 'y', "CSMSG_AUTOMODE_NORMAL" },
707 { 'v', "CSMSG_AUTOMODE_VOICE" },
708 { 'h', "CSMSG_AUTOMODE_HOP" },
709 { 'o', "CSMSG_AUTOMODE_OP" },
710 { 'm', "CSMSG_AUTOMODE_MUTE" },
711 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
712 }, protectValues
[] = {
713 { 'a', "CSMSG_PROTECT_ALL" },
714 { 'e', "CSMSG_PROTECT_EQUAL" },
715 { 'l', "CSMSG_PROTECT_LOWER" },
716 { 'n', "CSMSG_PROTECT_NONE" }
718 { 'd', "CSMSG_TOYS_DISABLED" },
719 { 'n', "CSMSG_TOYS_PRIVATE" },
720 { 'p', "CSMSG_TOYS_PUBLIC" }
721 }, topicRefreshValues
[] = {
722 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
723 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
724 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
725 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
726 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
727 }, ctcpReactionValues
[] = {
728 { 'n', "CSMSG_CTCPREACTION_NONE" },
729 { 'k', "CSMSG_CTCPREACTION_KICK" },
730 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
731 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
732 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
733 }, banTimeoutValues
[] = {
734 { '0', "CSMSG_BANTIMEOUT_NONE" },
735 { '1', "CSMSG_BANTIMEOUT_10M" },
736 { '2', "CSMSG_BANTIMEOUT_2H" },
737 { '3', "CSMSG_BANTIMEOUT_4H" },
738 { '4', "CSMSG_BANTIMEOUT_1D" },
739 { '5', "CSMSG_BANTIMEOUT_1W" }
742 { 'n', "CSMSG_RESYNC_NEVER" },
743 { '1', "CSMSG_RESYNC_3_HOURS" },
744 { '2', "CSMSG_RESYNC_6_HOURS" },
745 { '3', "CSMSG_RESYNC_12_HOURS" },
746 { '4', "CSMSG_RESYNC_24_HOURS" }
749 static const struct {
753 unsigned int old_idx
;
755 struct charOptionValues
*values
;
757 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
758 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
759 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
760 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
761 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
762 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
763 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
766 struct userData
*helperList
;
767 struct chanData
*channelList
;
768 static struct module *chanserv_module
;
769 static unsigned int userCount
;
770 unsigned int chanserv_read_version
= 0; /* db version control */
772 #define CHANSERV_DB_VERSION 2
774 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
775 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
778 user_level_from_name(const char *name
, unsigned short clamp_level
)
780 unsigned int level
= 0, ii
;
782 level
= strtoul(name
, NULL
, 10);
783 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
784 if(!irccasecmp(name
, accessLevels
[ii
].name
))
785 level
= accessLevels
[ii
].level
;
786 if(level
> clamp_level
)
792 user_level_name_from_level(int level
)
800 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
801 if(level
>= accessLevels
[ii
].level
)
802 highest
= accessLevels
[ii
].title
;
808 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
811 *minl
= strtoul(arg
, &sep
, 10);
819 *maxl
= strtoul(sep
+1, &sep
, 10);
827 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
829 struct userData
*uData
, **head
;
831 if(!channel
|| !handle
)
834 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
835 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
837 for(uData
= helperList
;
838 uData
&& uData
->handle
!= handle
;
839 uData
= uData
->next
);
843 uData
= calloc(1, sizeof(struct userData
));
844 uData
->handle
= handle
;
846 uData
->access
= UL_HELPER
;
852 uData
->next
= helperList
;
854 helperList
->prev
= uData
;
862 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
863 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
866 head
= &(channel
->users
);
869 if(uData
&& (uData
!= *head
))
871 /* Shuffle the user to the head of whatever list he was in. */
873 uData
->next
->prev
= uData
->prev
;
875 uData
->prev
->next
= uData
->next
;
881 (**head
).prev
= uData
;
888 /* Returns non-zero if user has at least the minimum access.
889 * exempt_owner is set when handling !set, so the owner can set things
892 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
894 struct userData
*uData
;
895 struct chanData
*cData
= channel
->channel_info
;
896 unsigned short minimum
= cData
->lvlOpts
[opt
];
899 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
902 if(minimum
<= uData
->access
)
904 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
909 /* Scan for other users authenticated to the same handle
910 still in the channel. If so, keep them listed as present.
912 user is optional, if not null, it skips checking that userNode
913 (for the handle_part function) */
915 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
919 if(IsSuspended(uData
->channel
)
920 || IsUserSuspended(uData
)
921 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
933 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, const char *text
, UNUSED_ARG(struct userNode
*bot
))
935 unsigned int eflags
, argc
;
937 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
939 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
940 if(!channel
->channel_info
941 || IsSuspended(channel
->channel_info
)
943 || !ircncasecmp(text
, "ACTION ", 7))
945 /* We dont punish people we know -Rubin
946 * * Figure out the minimum level needed to CTCP the channel *
948 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
951 /* If they are a user of the channel, they are exempt */
952 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
954 /* We need to enforce against them; do so. */
956 argv
[0] = (char*)text
;
957 argv
[1] = user
->nick
;
959 if(GetUserMode(channel
, user
))
960 eflags
|= ACTION_KICK
;
961 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
962 default: case 'n': return;
964 eflags
|= ACTION_KICK
;
967 eflags
|= ACTION_BAN
;
970 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
971 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
974 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
975 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
978 argv
[argc
++] = bad_ctcp_reason
;
979 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
983 chanserv_create_note_type(const char *name
)
985 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
986 strcpy(ntype
->name
, name
);
988 dict_insert(note_types
, ntype
->name
, ntype
);
993 chanserv_deref_note_type(void *data
)
995 struct note_type
*ntype
= data
;
997 if(--ntype
->refs
> 0)
1003 chanserv_flush_note_type(struct note_type
*ntype
)
1005 struct chanData
*cData
;
1006 for(cData
= channelList
; cData
; cData
= cData
->next
)
1007 dict_remove(cData
->notes
, ntype
->name
);
1011 chanserv_truncate_notes(struct note_type
*ntype
)
1013 struct chanData
*cData
;
1015 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1017 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1018 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1021 if(strlen(note
->note
) <= ntype
->max_length
)
1023 dict_remove2(cData
->notes
, ntype
->name
, 1);
1024 note
= realloc(note
, size
);
1025 note
->note
[ntype
->max_length
] = 0;
1026 dict_insert(cData
->notes
, ntype
->name
, note
);
1030 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1032 static struct note
*
1033 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1036 unsigned int len
= strlen(text
);
1038 if(len
> type
->max_length
) len
= type
->max_length
;
1039 note
= calloc(1, sizeof(*note
) + len
);
1041 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1042 memcpy(note
->note
, text
, len
);
1043 note
->note
[len
] = 0;
1044 dict_insert(channel
->notes
, type
->name
, note
);
1050 chanserv_free_note(void *data
)
1052 struct note
*note
= data
;
1054 chanserv_deref_note_type(note
->type
);
1055 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1059 static MODCMD_FUNC(cmd_createnote
) {
1060 struct note_type
*ntype
;
1061 unsigned int arg
= 1, existed
= 0, max_length
;
1063 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1066 ntype
= chanserv_create_note_type(argv
[arg
]);
1067 if(!irccasecmp(argv
[++arg
], "privileged"))
1070 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1071 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1073 else if(!irccasecmp(argv
[arg
], "channel"))
1075 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1078 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1081 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1082 ntype
->set_access
.min_ulevel
= ulvl
;
1084 else if(!irccasecmp(argv
[arg
], "setter"))
1086 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1090 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1094 if(!irccasecmp(argv
[++arg
], "privileged"))
1095 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1096 else if(!irccasecmp(argv
[arg
], "channel_users"))
1097 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1098 else if(!irccasecmp(argv
[arg
], "all"))
1099 ntype
->visible_type
= NOTE_VIS_ALL
;
1101 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1105 if((arg
+1) >= argc
) {
1106 reply("MSG_MISSING_PARAMS", argv
[0]);
1109 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1110 if(max_length
< 20 || max_length
> 450)
1112 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1115 if(existed
&& (max_length
< ntype
->max_length
))
1117 ntype
->max_length
= max_length
;
1118 chanserv_truncate_notes(ntype
);
1120 ntype
->max_length
= max_length
;
1123 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1125 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1130 dict_remove(note_types
, ntype
->name
);
1134 static MODCMD_FUNC(cmd_removenote
) {
1135 struct note_type
*ntype
;
1138 ntype
= dict_find(note_types
, argv
[1], NULL
);
1139 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1142 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1149 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1152 chanserv_flush_note_type(ntype
);
1154 dict_remove(note_types
, argv
[1]);
1155 reply("CSMSG_NOTE_DELETED", argv
[1]);
1160 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1164 if(orig
->modes_set
& change
->modes_clear
)
1166 if(orig
->modes_clear
& change
->modes_set
)
1168 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1169 && strcmp(orig
->new_key
, change
->new_key
))
1171 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1172 && (orig
->new_limit
!= change
->new_limit
))
1177 static char max_length_text
[MAXLEN
+1][16];
1179 static struct helpfile_expansion
1180 chanserv_expand_variable(const char *variable
)
1182 struct helpfile_expansion exp
;
1184 if(!irccasecmp(variable
, "notes"))
1187 exp
.type
= HF_TABLE
;
1188 exp
.value
.table
.length
= 1;
1189 exp
.value
.table
.width
= 3;
1190 exp
.value
.table
.flags
= 0;
1191 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1192 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1193 exp
.value
.table
.contents
[0][0] = "Note Type";
1194 exp
.value
.table
.contents
[0][1] = "Visibility";
1195 exp
.value
.table
.contents
[0][2] = "Max Length";
1196 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1198 struct note_type
*ntype
= iter_data(it
);
1201 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1202 row
= exp
.value
.table
.length
++;
1203 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1204 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1205 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1206 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1208 if(!max_length_text
[ntype
->max_length
][0])
1209 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1210 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1215 exp
.type
= HF_STRING
;
1216 exp
.value
.str
= NULL
;
1220 static struct chanData
*
1221 register_channel(struct chanNode
*cNode
, char *registrar
)
1223 struct chanData
*channel
;
1224 enum levelOption lvlOpt
;
1225 enum charOption chOpt
;
1227 channel
= calloc(1, sizeof(struct chanData
));
1229 channel
->notes
= dict_new();
1230 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1232 channel
->registrar
= strdup(registrar
);
1233 channel
->registered
= now
;
1234 channel
->visited
= now
;
1235 channel
->limitAdjusted
= now
;
1236 channel
->ownerTransfer
= now
;
1237 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1238 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1239 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1240 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1241 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1243 channel
->prev
= NULL
;
1244 channel
->next
= channelList
;
1247 channelList
->prev
= channel
;
1248 channelList
= channel
;
1249 registered_channels
++;
1251 channel
->channel
= cNode
;
1253 cNode
->channel_info
= channel
;
1258 static struct userData
*
1259 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
, time_t accessexpiry
)
1261 struct userData
*ud
;
1263 if(access
> UL_OWNER
)
1266 ud
= calloc(1, sizeof(*ud
));
1267 ud
->channel
= channel
;
1268 ud
->handle
= handle
;
1270 ud
->access
= access
;
1271 ud
->info
= info
? strdup(info
) : NULL
;
1272 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1277 ud
->next
= channel
->users
;
1279 channel
->users
->prev
= ud
;
1280 channel
->users
= ud
;
1282 channel
->userCount
++;
1286 ud
->u_next
= ud
->handle
->channels
;
1288 ud
->u_next
->u_prev
= ud
;
1289 ud
->handle
->channels
= ud
;
1291 ud
->flags
= USER_FLAGS_DEFAULT
;
1295 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1298 chanserv_expire_tempuser(void *data
)
1300 struct userData
*uData
= data
;
1304 handle
= strdup(uData
->handle
->handle
);
1305 if (uData
->accessexpiry
> 0) {
1306 if (uData
->present
) {
1307 struct userNode
*user
, *next_un
= NULL
;
1308 struct handle_info
*hi
;
1310 hi
= get_handle_info(handle
);
1311 for (user
= hi
->users
; user
; user
= next_un
) {
1312 struct mod_chanmode
*change
;
1313 struct modeNode
*mn
;
1314 unsigned int count
= 0;
1316 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1317 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1318 next_un
= user
->next_authed
;
1322 change
= mod_chanmode_alloc(2);
1323 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1324 change
->args
[count
++].u
.member
= mn
;
1327 change
->argc
= count
;
1328 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1330 mod_chanmode_free(change
);
1331 next_un
= user
->next_authed
;
1334 del_channel_user(uData
, 1);
1340 chanserv_expire_tempclvl(void *data
)
1342 struct userData
*uData
= data
;
1346 handle
= strdup(uData
->handle
->handle
);
1347 if (uData
->clvlexpiry
> 0) {
1348 int changemodes
= 0;
1349 unsigned int mode
= 0;
1351 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1353 mode
= MODE_REMOVE
| MODE_CHANOP
;
1354 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1356 mode
= MODE_REMOVE
| MODE_HALFOP
;
1360 if (uData
->present
) {
1361 struct userNode
*user
, *next_un
= NULL
;
1362 struct handle_info
*hi
;
1364 hi
= get_handle_info(handle
);
1365 for (user
= hi
->users
; user
; user
= next_un
) {
1366 struct mod_chanmode
*change
;
1367 struct modeNode
*mn
;
1368 unsigned int count
= 0;
1370 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1371 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1372 next_un
= user
->next_authed
;
1376 if (changemodes
== 0) {
1377 next_un
= user
->next_authed
;
1381 change
= mod_chanmode_alloc(2);
1382 change
->args
[count
].mode
= mode
;
1383 change
->args
[count
++].u
.member
= mn
;
1386 change
->argc
= count
;
1387 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1389 mod_chanmode_free(change
);
1390 next_un
= user
->next_authed
;
1394 uData
->access
= uData
->lastaccess
;
1395 uData
->lastaccess
= 0;
1396 uData
->clvlexpiry
= 0;
1402 del_channel_user(struct userData
*user
, int do_gc
)
1404 struct chanData
*channel
= user
->channel
;
1406 channel
->userCount
--;
1409 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1410 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1413 user
->prev
->next
= user
->next
;
1415 channel
->users
= user
->next
;
1417 user
->next
->prev
= user
->prev
;
1420 user
->u_prev
->u_next
= user
->u_next
;
1422 user
->handle
->channels
= user
->u_next
;
1424 user
->u_next
->u_prev
= user
->u_prev
;
1428 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1429 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1430 unregister_channel(channel
, "lost all users.");
1434 static struct adduserPending
*
1435 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1437 struct adduserPending
*ap
;
1438 ap
= calloc(1,sizeof(struct adduserPending
));
1439 ap
->channel
= channel
;
1442 ap
->created
= time(NULL
);
1444 /* ap->prev defaults to NULL already.. */
1445 ap
->next
= adduser_pendings
;
1446 if(adduser_pendings
)
1447 adduser_pendings
->prev
= ap
;
1448 adduser_pendings
= ap
;
1449 adduser_pendings_count
++;
1454 del_adduser_pending(struct adduserPending
*ap
)
1457 ap
->prev
->next
= ap
->next
;
1459 adduser_pendings
= ap
->next
;
1462 ap
->next
->prev
= ap
->prev
;
1466 static void expire_adduser_pending();
1468 /* find_adduser_pending(channel, user) will find an arbitrary record
1469 * from user, channel, or user and channel.
1470 * if user or channel are NULL, they will match any records.
1472 static struct adduserPending
*
1473 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1475 struct adduserPending
*ap
;
1477 expire_adduser_pending(); /* why not here.. */
1479 if(!channel
&& !user
) /* 2 nulls matches all */
1480 return(adduser_pendings
);
1481 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1483 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1490 /* Remove all pendings for a user or channel
1492 * called in nickserv.c DelUser() and proto-* unregister_channel()
1495 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1497 struct adduserPending
*ap
;
1499 /* So this is a bit wastefull, i hate dealing with linked lists.
1500 * if its a problem we'll rewrite it right */
1501 while((ap
= find_adduser_pending(channel
, user
))) {
1502 del_adduser_pending(ap
);
1506 /* Called from nickserv.c cmd_auth after someone auths */
1508 process_adduser_pending(struct userNode
*user
)
1510 struct adduserPending
*ap
;
1511 if(!user
->handle_info
)
1512 return; /* not associated with an account */
1513 while((ap
= find_adduser_pending(NULL
, user
)))
1515 struct userData
*actee
;
1516 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1518 /* Already on the userlist. do nothing*/
1522 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1523 scan_user_presence(actee
, NULL
);
1525 del_adduser_pending(ap
);
1530 expire_adduser_pending()
1532 struct adduserPending
*ap
, *ap_next
;
1533 ap
= adduser_pendings
;
1536 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1538 ap_next
= ap
->next
; /* save next */
1539 del_adduser_pending(ap
); /* free and relink */
1540 ap
= ap_next
; /* advance */
1547 static void expire_ban(void *data
);
1550 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1553 unsigned int ii
, l1
, l2
;
1558 bd
= malloc(sizeof(struct banData
));
1560 bd
->channel
= channel
;
1562 bd
->triggered
= triggered
;
1563 bd
->expires
= expires
;
1565 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1567 extern const char *hidden_host_suffix
;
1568 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1572 l2
= strlen(old_name
);
1575 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1577 new_mask
= alloca(MAXLEN
);
1578 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1581 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1583 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1584 bd
->reason
= strdup(reason
);
1587 timeq_add(expires
, expire_ban
, bd
);
1590 bd
->next
= channel
->bans
; /* lamers */
1592 channel
->bans
->prev
= bd
;
1594 channel
->banCount
++;
1601 del_channel_ban(struct banData
*ban
)
1603 ban
->channel
->banCount
--;
1607 ban
->prev
->next
= ban
->next
;
1609 ban
->channel
->bans
= ban
->next
;
1612 ban
->next
->prev
= ban
->prev
;
1615 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1624 expire_ban(void *data
) /* lamer.. */
1626 struct banData
*bd
= data
;
1627 if(!IsSuspended(bd
->channel
))
1629 struct banList bans
;
1630 struct mod_chanmode change
;
1632 bans
= bd
->channel
->channel
->banlist
;
1633 mod_chanmode_init(&change
);
1634 for(ii
=0; ii
<bans
.used
; ii
++)
1636 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1639 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1640 change
.args
[0].u
.hostmask
= bd
->mask
;
1641 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1647 del_channel_ban(bd
);
1650 static void chanserv_expire_suspension(void *data
);
1653 unregister_channel(struct chanData
*channel
, const char *reason
)
1655 struct mod_chanmode change
;
1656 char msgbuf
[MAXLEN
];
1658 /* After channel unregistration, the following must be cleaned
1660 - Channel information.
1662 - Channel bans. (lamers)
1663 - Channel suspension data.
1664 - adduser_pending data.
1665 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1671 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1675 mod_chanmode_init(&change
);
1676 change
.modes_clear
|= MODE_REGISTERED
;
1677 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1680 wipe_adduser_pending(channel
->channel
, NULL
);
1682 while(channel
->users
)
1683 del_channel_user(channel
->users
, 0);
1685 while(channel
->bans
)
1686 del_channel_ban(channel
->bans
);
1688 free(channel
->topic
);
1689 free(channel
->registrar
);
1690 free(channel
->greeting
);
1691 free(channel
->user_greeting
);
1692 free(channel
->topic_mask
);
1695 channel
->prev
->next
= channel
->next
;
1697 channelList
= channel
->next
;
1700 channel
->next
->prev
= channel
->prev
;
1702 if(channel
->suspended
)
1704 struct chanNode
*cNode
= channel
->channel
;
1705 struct suspended
*suspended
, *next_suspended
;
1707 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1709 next_suspended
= suspended
->previous
;
1710 free(suspended
->suspender
);
1711 free(suspended
->reason
);
1712 if(suspended
->expires
)
1713 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1718 cNode
->channel_info
= NULL
;
1720 channel
->channel
->channel_info
= NULL
;
1722 dict_delete(channel
->notes
);
1723 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1724 if(!IsSuspended(channel
))
1725 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1726 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1727 UnlockChannel(channel
->channel
);
1729 registered_channels
--;
1733 expire_channels(UNUSED_ARG(void *data
))
1735 struct chanData
*channel
, *next
;
1736 struct userData
*user
;
1737 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1739 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1740 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1742 for(channel
= channelList
; channel
; channel
= next
)
1744 next
= channel
->next
;
1746 /* See if the channel can be expired. */
1747 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1748 || IsProtected(channel
))
1751 /* Make sure there are no high-ranking users still in the channel. */
1752 for(user
=channel
->users
; user
; user
=user
->next
)
1753 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1758 /* Unregister the channel */
1759 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1760 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1761 unregister_channel(channel
, "registration expired.");
1764 if(chanserv_conf
.channel_expire_frequency
)
1765 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1769 expire_dnrs(UNUSED_ARG(void *data
))
1772 struct do_not_register
*dnr
;
1774 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1776 dnr
= iter_data(it
);
1777 if(!dnr
->expires
|| dnr
->expires
> now
)
1779 dict_remove(handle_dnrs
, dnr
->chan_name
+ 1);
1781 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1783 dnr
= iter_data(it
);
1784 if(!dnr
->expires
|| dnr
->expires
> now
)
1786 dict_remove(plain_dnrs
, dnr
->chan_name
);
1788 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1790 dnr
= iter_data(it
);
1791 if(!dnr
->expires
|| dnr
->expires
> now
)
1793 dict_remove(mask_dnrs
, dnr
->chan_name
);
1796 if(chanserv_conf
.dnr_expire_frequency
)
1797 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
1801 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1803 char protect
= channel
->chOpts
[chProtect
];
1804 struct userData
*cs_victim
, *cs_aggressor
;
1806 /* If victim access level is greater than set invitelevel, don't let
1807 * us kick them, but don't consider it punishment if someone else does
1811 if(victim
== aggressor
)
1813 /* Don't protect if the victim isn't authenticated (because they
1814 can't be a channel user), unless we are to protect non-users
1817 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1819 /* If they have enough access to invite themselvs through a ban,
1820 * and its us kicking them, don't. -Rubin */
1821 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1827 if(protect
!= 'a' && !cs_victim
)
1830 /* Protect if the aggressor isn't a user because at this point,
1831 the aggressor can only be less than or equal to the victim. */
1833 /* Not protected from chanserv except above */
1834 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1835 if(aggressor
== chanserv
)
1838 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1842 /* If the aggressor was a user, then the victim can't be helped. */
1849 if(cs_victim
->access
> cs_aggressor
->access
)
1854 if(cs_victim
->access
>= cs_aggressor
->access
)
1863 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1865 struct chanData
*cData
= channel
->channel_info
;
1866 struct userData
*cs_victim
;
1868 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1869 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1870 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1873 reply("CSMSG_OPBY_LOCKED");
1875 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1883 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1885 struct chanData
*cData
= channel
->channel_info
;
1886 struct userData
*cs_victim
;
1888 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1889 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1890 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1892 reply("CSMSG_HOPBY_LOCKED");
1901 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1903 if(IsService(victim
))
1905 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1909 if(protect_user(victim
, user
, channel
->channel_info
, false))
1911 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1919 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1921 if(IsService(victim
))
1923 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1927 if(protect_user(victim
, user
, channel
->channel_info
, false))
1929 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1936 static struct do_not_register
*
1937 chanserv_add_dnr(const char *chan_name
, const char *setter
, time_t expires
, const char *reason
)
1939 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1940 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1941 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1942 strcpy(dnr
->reason
, reason
);
1944 dnr
->expires
= expires
;
1945 if(dnr
->chan_name
[0] == '*')
1946 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1947 else if(strpbrk(dnr
->chan_name
, "*?"))
1948 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1950 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1954 static struct dnrList
1955 chanserv_find_dnrs(const char *chan_name
, const char *handle
, unsigned int max
)
1957 struct dnrList list
;
1958 dict_iterator_t it
, next
;
1959 struct do_not_register
*dnr
;
1961 dnrList_init(&list
);
1963 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1965 if(dnr
->expires
&& dnr
->expires
<= now
)
1966 dict_remove(handle_dnrs
, handle
);
1967 else if(list
.used
< max
)
1968 dnrList_append(&list
, dnr
);
1971 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1973 if(dnr
->expires
&& dnr
->expires
<= now
)
1974 dict_remove(plain_dnrs
, chan_name
);
1975 else if(list
.used
< max
)
1976 dnrList_append(&list
, dnr
);
1981 for(it
= dict_first(mask_dnrs
); it
&& list
.used
< max
; it
= next
)
1983 next
= iter_next(it
);
1984 if(!match_ircglob(chan_name
, iter_key(it
)))
1986 dnr
= iter_data(it
);
1987 if(dnr
->expires
&& dnr
->expires
<= now
)
1988 dict_remove(mask_dnrs
, iter_key(it
));
1990 dnrList_append(&list
, dnr
);
1997 static int dnr_print_func(struct do_not_register
*dnr
, void *extra
)
1999 struct userNode
*user
;
2000 char buf1
[INTERVALLEN
];
2001 char buf2
[INTERVALLEN
];
2005 strftime(buf1
, sizeof(buf1
), "%d %b %Y", localtime(&dnr
->set
));
2008 strftime(buf2
, sizeof(buf2
), "%d %b %Y", localtime(&dnr
->expires
));
2009 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET_EXPIRES", dnr
->chan_name
, buf1
, dnr
->setter
, buf2
, dnr
->reason
);
2013 send_message(user
, chanserv
, "CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf1
, dnr
->setter
, dnr
->reason
);
2016 send_message(user
, chanserv
, "CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2021 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
2023 struct dnrList list
;
2026 list
= chanserv_find_dnrs(chan_name
, handle
, UINT_MAX
);
2027 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
2028 dnr_print_func(list
.list
[ii
], user
);
2031 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
2036 struct do_not_register
*
2037 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
2039 struct dnrList list
;
2040 struct do_not_register
*dnr
;
2042 list
= chanserv_find_dnrs(chan_name
, handle
? handle
->handle
: NULL
, 1);
2043 dnr
= list
.used
? list
.list
[0] : NULL
;
2049 static unsigned int send_dnrs(struct userNode
*user
, dict_t dict
)
2051 struct do_not_register
*dnr
;
2052 dict_iterator_t it
, next
;
2053 unsigned int matches
= 0;
2055 for(it
= dict_first(dict
); it
; it
= next
)
2057 dnr
= iter_data(it
);
2058 next
= iter_next(it
);
2059 if(dnr
->expires
&& dnr
->expires
<= now
)
2061 dict_remove(dict
, iter_key(it
));
2064 dnr_print_func(dnr
, user
);
2071 static CHANSERV_FUNC(cmd_noregister
)
2074 time_t expiry
, duration
;
2075 unsigned int matches
;
2079 reply("CSMSG_DNR_SEARCH_RESULTS");
2080 matches
= send_dnrs(user
, handle_dnrs
);
2081 matches
+= send_dnrs(user
, plain_dnrs
);
2082 matches
+= send_dnrs(user
, mask_dnrs
);
2084 reply("MSG_MATCH_COUNT", matches
);
2086 reply("MSG_NO_MATCHES");
2092 if(!IsChannelName(target
) && (*target
!= '*'))
2094 reply("CSMSG_NOT_DNR", target
);
2102 reply("MSG_INVALID_DURATION", argv
[2]);
2106 if(!strcmp(argv
[2], "0"))
2108 else if((duration
= ParseInterval(argv
[2])))
2109 expiry
= now
+ duration
;
2112 reply("MSG_INVALID_DURATION", argv
[2]);
2116 const char *reason
= unsplit_string(argv
+ 3, argc
- 3, NULL
);
2117 if((*target
== '*') && !get_handle_info(target
+ 1))
2119 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2122 chanserv_add_dnr(target
, user
->handle_info
->handle
, expiry
, reason
);
2123 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2127 reply("CSMSG_DNR_SEARCH_RESULTS");
2129 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2131 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2133 reply("MSG_NO_MATCHES");
2137 static CHANSERV_FUNC(cmd_allowregister
)
2139 const char *chan_name
= argv
[1];
2141 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2142 || dict_remove(plain_dnrs
, chan_name
)
2143 || dict_remove(mask_dnrs
, chan_name
))
2145 reply("CSMSG_DNR_REMOVED", chan_name
);
2148 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2153 struct userNode
*source
;
2157 time_t min_set
, max_set
;
2158 time_t min_expires
, max_expires
;
2163 dnr_search_matches(const struct do_not_register
*dnr
, const struct dnr_search
*search
)
2165 return !((dnr
->set
< search
->min_set
)
2166 || (dnr
->set
> search
->max_set
)
2167 || (dnr
->expires
&& ((dnr
->expires
< search
->min_expires
)
2168 || (dnr
->expires
> search
->max_expires
)))
2169 || (search
->chan_mask
2170 && !match_ircglob(dnr
->chan_name
, search
->chan_mask
))
2171 || (search
->setter_mask
2172 && !match_ircglob(dnr
->setter
, search
->setter_mask
))
2173 || (search
->reason_mask
2174 && !match_ircglob(dnr
->reason
, search
->reason_mask
)));
2177 static struct dnr_search
*
2178 dnr_search_create(struct userNode
*user
, struct svccmd
*cmd
, unsigned int argc
, char *argv
[])
2180 struct dnr_search
*discrim
;
2183 discrim
= calloc(1, sizeof(*discrim
));
2184 discrim
->source
= user
;
2185 discrim
->chan_mask
= NULL
;
2186 discrim
->setter_mask
= NULL
;
2187 discrim
->reason_mask
= NULL
;
2188 discrim
->max_set
= INT_MAX
;
2189 discrim
->max_expires
= INT_MAX
;
2190 discrim
->limit
= 50;
2192 for(ii
=0; ii
<argc
; ++ii
)
2196 reply("MSG_MISSING_PARAMS", argv
[ii
]);
2199 else if(0 == irccasecmp(argv
[ii
], "channel"))
2201 discrim
->chan_mask
= argv
[++ii
];
2203 else if(0 == irccasecmp(argv
[ii
], "setter"))
2205 discrim
->setter_mask
= argv
[++ii
];
2207 else if(0 == irccasecmp(argv
[ii
], "reason"))
2209 discrim
->reason_mask
= argv
[++ii
];
2211 else if(0 == irccasecmp(argv
[ii
], "limit"))
2213 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 0);
2215 else if(0 == irccasecmp(argv
[ii
], "set"))
2217 const char *cmp
= argv
[++ii
];
2220 discrim
->min_set
= now
- ParseInterval(cmp
+ 2);
2222 discrim
->min_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2223 } else if(cmp
[0] == '=') {
2224 discrim
->min_set
= discrim
->max_set
= now
- ParseInterval(cmp
+ 1);
2225 } else if(cmp
[0] == '>') {
2227 discrim
->max_set
= now
- ParseInterval(cmp
+ 2);
2229 discrim
->max_set
= now
- (ParseInterval(cmp
+ 1) - 1);
2231 discrim
->max_set
= now
- (ParseInterval(cmp
) - 1);
2234 else if(0 == irccasecmp(argv
[ii
], "expires"))
2236 const char *cmp
= argv
[++ii
];
2239 discrim
->max_expires
= now
+ ParseInterval(cmp
+ 2);
2241 discrim
->max_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2242 } else if(cmp
[0] == '=') {
2243 discrim
->min_expires
= discrim
->max_expires
= now
+ ParseInterval(cmp
+ 1);
2244 } else if(cmp
[0] == '>') {
2246 discrim
->min_expires
= now
+ ParseInterval(cmp
+ 2);
2248 discrim
->min_expires
= now
+ (ParseInterval(cmp
+ 1) - 1);
2250 discrim
->min_expires
= now
+ (ParseInterval(cmp
) - 1);
2255 reply("MSG_INVALID_CRITERIA", argv
[ii
]);
2266 typedef int (*dnr_search_func
)(struct do_not_register
*match
, void *extra
);
2269 dnr_search(struct dnr_search
*discrim
, dnr_search_func dsf
, void *data
)
2271 struct do_not_register
*dnr
;
2272 dict_iterator_t next
;
2277 /* Initialize local variables. */
2280 if(discrim
->chan_mask
)
2282 int shift
= (discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*') ? 2 : 0;
2283 if('\0' == discrim
->chan_mask
[shift
+ strcspn(discrim
->chan_mask
+shift
, "*?")])
2287 if(target_fixed
&& discrim
->chan_mask
[0] == '\\' && discrim
->chan_mask
[1] == '*')
2289 /* Check against account-based DNRs. */
2290 dnr
= dict_find(handle_dnrs
, discrim
->chan_mask
+ 2, NULL
);
2291 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2294 else if(target_fixed
)
2296 /* Check against channel-based DNRs. */
2297 dnr
= dict_find(plain_dnrs
, discrim
->chan_mask
, NULL
);
2298 if(dnr
&& dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
))
2303 /* Exhaustively search account DNRs. */
2304 for(it
= dict_first(handle_dnrs
); it
; it
= next
)
2306 next
= iter_next(it
);
2307 dnr
= iter_data(it
);
2308 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2311 /* Do the same for channel DNRs. */
2312 for(it
= dict_first(plain_dnrs
); it
; it
= next
)
2314 next
= iter_next(it
);
2315 dnr
= iter_data(it
);
2316 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2320 /* Do the same for wildcarded channel DNRs. */
2321 for(it
= dict_first(mask_dnrs
); it
; it
= next
)
2323 next
= iter_next(it
);
2324 dnr
= iter_data(it
);
2325 if(dnr_search_matches(dnr
, discrim
) && (count
++ < discrim
->limit
) && dsf(dnr
, data
))
2333 dnr_remove_func(struct do_not_register
*match
, void *extra
)
2335 struct userNode
*user
;
2338 chan_name
= alloca(strlen(match
->chan_name
) + 1);
2339 strcpy(chan_name
, match
->chan_name
);
2341 if(((chan_name
[0] == '*') && dict_remove(handle_dnrs
, chan_name
+1))
2342 || dict_remove(plain_dnrs
, chan_name
)
2343 || dict_remove(mask_dnrs
, chan_name
))
2345 send_message(user
, chanserv
, "CSMSG_DNR_REMOVED", chan_name
);
2351 dnr_count_func(struct do_not_register
*match
, void *extra
)
2353 return 0; (void)match
; (void)extra
;
2356 static MODCMD_FUNC(cmd_dnrsearch
)
2358 struct dnr_search
*discrim
;
2359 dnr_search_func action
;
2360 struct svccmd
*subcmd
;
2361 unsigned int matches
;
2364 sprintf(buf
, "dnrsearch %s", argv
[1]);
2365 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
2368 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2371 if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
2373 if(!irccasecmp(argv
[1], "print"))
2374 action
= dnr_print_func
;
2375 else if(!irccasecmp(argv
[1], "remove"))
2376 action
= dnr_remove_func
;
2377 else if(!irccasecmp(argv
[1], "count"))
2378 action
= dnr_count_func
;
2381 reply("CSMSG_DNR_BAD_ACTION", argv
[1]);
2385 discrim
= dnr_search_create(user
, cmd
, argc
-2, argv
+2);
2389 if(action
== dnr_print_func
)
2390 reply("CSMSG_DNR_SEARCH_RESULTS");
2391 matches
= dnr_search(discrim
, action
, user
);
2393 reply("MSG_MATCH_COUNT", matches
);
2395 reply("MSG_NO_MATCHES");
2401 chanserv_get_owned_count(struct handle_info
*hi
)
2403 struct userData
*cList
;
2406 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2407 if(cList
->access
== UL_OWNER
)
2412 static CHANSERV_FUNC(cmd_register
)
2414 struct handle_info
*handle
;
2415 struct chanData
*cData
;
2416 struct modeNode
*mn
;
2417 char reason
[MAXLEN
];
2419 unsigned int new_channel
, force
=0;
2420 struct do_not_register
*dnr
;
2423 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2424 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2430 if(channel
->channel_info
)
2432 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2436 if(channel
->bad_channel
)
2438 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2442 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2444 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2449 chan_name
= channel
->name
;
2455 reply("MSG_MISSING_PARAMS", cmd
->name
);
2456 svccmd_send_help_brief(user
, chanserv
, cmd
);
2459 if(!IsChannelName(argv
[1]))
2461 reply("MSG_NOT_CHANNEL_NAME");
2465 if(opserv_bad_channel(argv
[1]))
2467 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2472 chan_name
= argv
[1];
2475 if(argc
>= (new_channel
+2))
2477 if(!IsHelping(user
))
2479 reply("CSMSG_PROXY_FORBIDDEN");
2483 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2485 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2486 dnr
= chanserv_is_dnr(chan_name
, handle
);
2488 /* Check if they are over the limit.. */
2489 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2491 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2498 handle
= user
->handle_info
;
2499 dnr
= chanserv_is_dnr(chan_name
, handle
);
2500 /* Check if they are over the limit.. */
2501 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2503 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2506 /* Check if another service is in the channel */
2508 for(n
= 0; n
< channel
->members
.used
; n
++)
2510 mn
= channel
->members
.list
[n
];
2511 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2513 reply("CSMSG_ANOTHER_SERVICE");
2520 if(!IsHelping(user
))
2521 reply("CSMSG_DNR_CHANNEL", chan_name
);
2523 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2527 /* now handled above for message specilization *
2528 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2530 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2536 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2538 cData
= register_channel(channel
, user
->handle_info
->handle
);
2539 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2540 cData
->modes
= chanserv_conf
.default_modes
;
2542 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2543 if (IsOffChannel(cData
))
2545 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2549 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2550 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2551 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2553 mod_chanmode_announce(chanserv
, channel
, change
);
2554 mod_chanmode_free(change
);
2557 /* Initialize the channel's max user record. */
2558 cData
->max
= channel
->members
.used
;
2560 if(handle
!= user
->handle_info
)
2561 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2564 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2565 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2566 handle
->handle
, user
->handle_info
->handle
);
2571 make_confirmation_string(struct userData
*uData
)
2573 static char strbuf
[16];
2578 for(src
= uData
->handle
->handle
; *src
; )
2579 accum
= accum
* 31 + toupper(*src
++);
2581 for(src
= uData
->channel
->channel
->name
; *src
; )
2582 accum
= accum
* 31 + toupper(*src
++);
2583 sprintf(strbuf
, "%08x", accum
);
2587 static CHANSERV_FUNC(cmd_unregister
)
2590 char reason
[MAXLEN
];
2591 struct chanData
*cData
;
2592 struct userData
*uData
;
2594 cData
= channel
->channel_info
;
2597 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2601 uData
= GetChannelUser(cData
, user
->handle_info
);
2602 if(!uData
|| (uData
->access
< UL_OWNER
))
2604 reply("CSMSG_NO_ACCESS");
2608 if(IsProtected(cData
))
2610 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2614 if(!IsHelping(user
))
2616 const char *confirm_string
;
2617 if(IsSuspended(cData
))
2619 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2622 confirm_string
= make_confirmation_string(uData
);
2623 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2625 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2630 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2631 name
= strdup(channel
->name
);
2632 unregister_channel(cData
, reason
);
2633 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2634 reply("CSMSG_UNREG_SUCCESS", name
);
2640 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2642 extern struct userNode
*spamserv
;
2643 struct mod_chanmode
*change
;
2645 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2647 change
= mod_chanmode_alloc(2);
2649 change
->args
[0].mode
= MODE_CHANOP
;
2650 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2651 change
->args
[1].mode
= MODE_CHANOP
;
2652 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2656 change
= mod_chanmode_alloc(1);
2658 change
->args
[0].mode
= MODE_CHANOP
;
2659 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2662 mod_chanmode_announce(chanserv
, channel
, change
);
2663 mod_chanmode_free(change
);
2666 static CHANSERV_FUNC(cmd_move
)
2668 struct mod_chanmode change
;
2669 struct chanNode
*target
;
2670 struct modeNode
*mn
;
2671 struct userData
*uData
;
2672 struct do_not_register
*dnr
;
2673 int chanserv_join
= 0, spamserv_join
;
2677 if(IsProtected(channel
->channel_info
))
2679 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2683 if(!IsChannelName(argv
[1]))
2685 reply("MSG_NOT_CHANNEL_NAME");
2689 if(opserv_bad_channel(argv
[1]))
2691 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2695 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2697 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2699 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2701 if(!IsHelping(user
))
2702 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2704 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2710 mod_chanmode_init(&change
);
2711 if(!(target
= GetChannel(argv
[1])))
2713 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2714 if(!IsSuspended(channel
->channel_info
))
2717 else if(target
->channel_info
)
2719 reply("CSMSG_ALREADY_REGGED", target
->name
);
2722 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2723 && !IsHelping(user
))
2725 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2728 else if(!IsSuspended(channel
->channel_info
))
2733 /* Clear MODE_REGISTERED from old channel, add it to new. */
2735 change
.modes_clear
= MODE_REGISTERED
;
2736 mod_chanmode_announce(chanserv
, channel
, &change
);
2737 change
.modes_clear
= 0;
2738 change
.modes_set
= MODE_REGISTERED
;
2739 mod_chanmode_announce(chanserv
, target
, &change
);
2742 /* Move the channel_info to the target channel; it
2743 shouldn't be necessary to clear timeq callbacks
2744 for the old channel. */
2745 target
->channel_info
= channel
->channel_info
;
2746 target
->channel_info
->channel
= target
;
2747 channel
->channel_info
= NULL
;
2749 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2752 ss_cs_join_channel(target
, spamserv_join
);
2754 if(!IsSuspended(target
->channel_info
))
2756 char reason2
[MAXLEN
];
2757 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2758 DelChannelUser(chanserv
, channel
, reason2
, 0);
2761 UnlockChannel(channel
);
2762 LockChannel(target
);
2763 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2764 channel
->name
, target
->name
, user
->handle_info
->handle
);
2766 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2771 merge_users(struct chanData
*source
, struct chanData
*target
)
2773 struct userData
*suData
, *tuData
, *next
;
2779 /* Insert the source's users into the scratch area. */
2780 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2781 dict_insert(merge
, suData
->handle
->handle
, suData
);
2783 /* Iterate through the target's users, looking for
2784 users common to both channels. The lower access is
2785 removed from either the scratch area or target user
2787 for(tuData
= target
->users
; tuData
; tuData
= next
)
2789 struct userData
*choice
;
2791 next
= tuData
->next
;
2793 /* If a source user exists with the same handle as a target
2794 channel's user, resolve the conflict by removing one. */
2795 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2799 /* Pick the data we want to keep. */
2800 /* If the access is the same, use the later seen time. */
2801 if(suData
->access
== tuData
->access
)
2802 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2803 else /* Otherwise, keep the higher access level. */
2804 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2806 /* Remove the user that wasn't picked. */
2807 if(choice
== tuData
)
2809 dict_remove(merge
, suData
->handle
->handle
);
2810 del_channel_user(suData
, 0);
2813 del_channel_user(tuData
, 0);
2816 /* Move the remaining users to the target channel. */
2817 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2819 suData
= iter_data(it
);
2821 /* Insert the user into the target channel's linked list. */
2822 suData
->prev
= NULL
;
2823 suData
->next
= target
->users
;
2824 suData
->channel
= target
;
2827 target
->users
->prev
= suData
;
2828 target
->users
= suData
;
2830 /* Update the user counts for the target channel; the
2831 source counts are left alone. */
2832 target
->userCount
++;
2835 /* Possible to assert (source->users == NULL) here. */
2836 source
->users
= NULL
;
2841 merge_bans(struct chanData
*source
, struct chanData
*target
)
2843 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2845 /* Hold on to the original head of the target ban list
2846 to avoid comparing source bans with source bans. */
2847 tFront
= target
->bans
;
2849 /* Perform a totally expensive O(n*m) merge, ick. */
2850 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2852 /* Flag to track whether the ban's been moved
2853 to the destination yet. */
2856 /* Possible to assert (sbData->prev == NULL) here. */
2857 sNext
= sbData
->next
;
2859 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2861 tNext
= tbData
->next
;
2863 /* Perform two comparisons between each source
2864 and target ban, conflicts are resolved by
2865 keeping the broader ban and copying the later
2866 expiration and triggered time. */
2867 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2869 /* There is a broader ban in the target channel that
2870 overrides one in the source channel; remove the
2871 source ban and break. */
2872 if(sbData
->expires
> tbData
->expires
)
2873 tbData
->expires
= sbData
->expires
;
2874 if(sbData
->triggered
> tbData
->triggered
)
2875 tbData
->triggered
= sbData
->triggered
;
2876 del_channel_ban(sbData
);
2879 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2881 /* There is a broader ban in the source channel that
2882 overrides one in the target channel; remove the
2883 target ban, fall through and move the source over. */
2884 if(tbData
->expires
> sbData
->expires
)
2885 sbData
->expires
= tbData
->expires
;
2886 if(tbData
->triggered
> sbData
->triggered
)
2887 sbData
->triggered
= tbData
->triggered
;
2888 if(tbData
== tFront
)
2890 del_channel_ban(tbData
);
2893 /* Source bans can override multiple target bans, so
2894 we allow a source to run through this loop multiple
2895 times, but we can only move it once. */
2900 /* Remove the source ban from the source ban list. */
2902 sbData
->next
->prev
= sbData
->prev
;
2904 /* Modify the source ban's associated channel. */
2905 sbData
->channel
= target
;
2907 /* Insert the ban into the target channel's linked list. */
2908 sbData
->prev
= NULL
;
2909 sbData
->next
= target
->bans
;
2912 target
->bans
->prev
= sbData
;
2913 target
->bans
= sbData
;
2915 /* Update the user counts for the target channel. */
2920 /* Possible to assert (source->bans == NULL) here. */
2921 source
->bans
= NULL
;
2925 merge_data(struct chanData
*source
, struct chanData
*target
)
2927 /* Use more recent visited and owner-transfer time; use older
2928 * registered time. Bitwise or may_opchan. Use higher max.
2929 * Do not touch last_refresh, ban count or user counts.
2931 if(source
->visited
> target
->visited
)
2932 target
->visited
= source
->visited
;
2933 if(source
->registered
< target
->registered
)
2934 target
->registered
= source
->registered
;
2935 if(source
->ownerTransfer
> target
->ownerTransfer
)
2936 target
->ownerTransfer
= source
->ownerTransfer
;
2937 if(source
->may_opchan
)
2938 target
->may_opchan
= 1;
2939 if(source
->max
> target
->max
)
2940 target
->max
= source
->max
;
2944 merge_channel(struct chanData
*source
, struct chanData
*target
)
2946 merge_users(source
, target
);
2947 merge_bans(source
, target
);
2948 merge_data(source
, target
);
2951 static CHANSERV_FUNC(cmd_merge
)
2953 struct userData
*target_user
;
2954 struct chanNode
*target
;
2955 char reason
[MAXLEN
];
2959 /* Make sure the target channel exists and is registered to the user
2960 performing the command. */
2961 if(!(target
= GetChannel(argv
[1])))
2963 reply("MSG_INVALID_CHANNEL");
2967 if(!target
->channel_info
)
2969 reply("CSMSG_NOT_REGISTERED", target
->name
);
2973 if(IsProtected(channel
->channel_info
))
2975 reply("CSMSG_MERGE_NODELETE");
2979 if(IsSuspended(target
->channel_info
))
2981 reply("CSMSG_MERGE_SUSPENDED");
2985 if(channel
== target
)
2987 reply("CSMSG_MERGE_SELF");
2991 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2992 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2994 reply("CSMSG_MERGE_NOT_OWNER");
2998 /* Merge the channel structures and associated data. */
2999 merge_channel(channel
->channel_info
, target
->channel_info
);
3000 spamserv_cs_move_merge(user
, channel
, target
, 0);
3001 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
3002 unregister_channel(channel
->channel_info
, reason
);
3003 reply("CSMSG_MERGE_SUCCESS", target
->name
);
3007 static CHANSERV_FUNC(cmd_opchan
)
3009 struct mod_chanmode change
;
3010 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
3012 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
3015 channel
->channel_info
->may_opchan
= 0;
3016 mod_chanmode_init(&change
);
3018 change
.args
[0].mode
= MODE_CHANOP
;
3019 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
3020 mod_chanmode_announce(chanserv
, channel
, &change
);
3021 reply("CSMSG_OPCHAN_DONE", channel
->name
);
3025 static CHANSERV_FUNC(cmd_adduser
)
3027 struct userData
*actee
;
3028 struct userData
*actor
, *real_actor
;;
3029 struct handle_info
*handle
;
3030 unsigned short access
, override
= 0;
3034 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
3036 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
3040 access
= user_level_from_name(argv
[2], UL_OWNER
);
3043 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3047 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3048 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3050 if(actor
->access
<= access
)
3052 reply("CSMSG_NO_BUMP_ACCESS");
3056 /* Trying to add someone with equal/more access? */
3057 if (!real_actor
|| real_actor
->access
<= access
)
3058 override
= CMD_LOG_OVERRIDE
;
3060 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3062 // 'kevin must first authenticate with AuthServ.' is sent to user
3063 struct userNode
*unode
;
3064 unode
= GetUserH(argv
[1]); /* find user struct by nick */
3067 if(find_adduser_pending(channel
, unode
)) {
3068 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
3071 if(IsInChannel(channel
, unode
)) {
3072 reply("CSMSG_ADDUSER_PENDING");
3073 add_adduser_pending(channel
, unode
, access
);
3074 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
3076 /* this results in user must auth AND not in chan errors. too confusing..
3078 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3086 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3088 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
3092 time_t accessexpiry
= 0;
3093 unsigned int duration
= 0;
3095 if ((duration
= ParseInterval(argv
[3])))
3096 accessexpiry
= now
+ duration
;
3099 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
, accessexpiry
);
3100 scan_user_presence(actee
, NULL
);
3103 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
3105 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
3106 return 1 | override
;
3109 static CHANSERV_FUNC(cmd_clvl
)
3111 struct handle_info
*handle
;
3112 struct userData
*victim
;
3113 struct userData
*actor
, *real_actor
;
3114 unsigned short new_access
, override
= 0;
3115 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3119 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3120 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3122 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
3125 if(handle
== user
->handle_info
&& !privileged
)
3127 reply("CSMSG_NO_SELF_CLVL");
3131 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3133 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3137 if(actor
->access
<= victim
->access
&& !privileged
)
3139 reply("MSG_USER_OUTRANKED", handle
->handle
);
3143 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
3147 reply("CSMSG_INVALID_ACCESS", argv
[2]);
3151 if(new_access
>= actor
->access
&& !privileged
)
3153 reply("CSMSG_NO_BUMP_ACCESS");
3157 time_t clvlexpiry
= 0;
3158 unsigned int duration
= 0;
3160 if ((duration
= ParseInterval(argv
[3])))
3161 clvlexpiry
= now
+ duration
;
3165 if (victim
->accessexpiry
> 0) {
3166 reply("CSMSG_NO_BUMP_EXPIRY");
3170 victim
->clvlexpiry
= clvlexpiry
;
3171 victim
->lastaccess
= victim
->access
;
3172 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
3175 /* Trying to clvl a equal/higher user? */
3176 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& handle
!= user
->handle_info
))
3177 override
= CMD_LOG_OVERRIDE
;
3178 /* Trying to clvl someone to equal/higher access? */
3179 if(!real_actor
|| new_access
>= real_actor
->access
)
3180 override
= CMD_LOG_OVERRIDE
;
3181 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3182 * If they lower their own access it's not a big problem.
3185 victim
->access
= new_access
;
3186 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
3188 return 1 | override
;
3191 static CHANSERV_FUNC(cmd_deluser
)
3193 struct handle_info
*handle
;
3194 struct userData
*victim
;
3195 struct userData
*actor
, *real_actor
;
3196 unsigned short access
, override
= 0;
3201 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3202 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3204 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
3207 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
3209 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
3215 access
= user_level_from_name(argv
[1], UL_OWNER
);
3216 char *useraccess
= user_level_name_from_level(victim
->access
);
3219 reply("CSMSG_INVALID_ACCESS", argv
[1]);
3222 if(strcasecmp(argv
[1], useraccess
))
3224 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
3230 access
= victim
->access
;
3233 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
3235 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
3239 /* If people delete themselves it is an override, but they
3240 * could've used deleteme so we don't log it as an override
3242 if(!real_actor
|| (real_actor
->access
<= victim
->access
&& real_actor
!= victim
))
3243 override
= CMD_LOG_OVERRIDE
;
3245 chan_name
= strdup(channel
->name
);
3246 del_channel_user(victim
, 1);
3247 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
3250 return 1 | override
;
3254 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
3256 struct userData
*actor
, *real_actor
, *uData
, *next
;
3257 unsigned int override
= 0;
3259 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3260 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3262 if(min_access
> max_access
)
3264 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3268 if((actor
->access
<= max_access
) && !IsHelping(user
))
3270 reply("CSMSG_NO_ACCESS");
3274 if(!real_actor
|| real_actor
->access
<= max_access
)
3275 override
= CMD_LOG_OVERRIDE
;
3277 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3281 if((uData
->access
>= min_access
)
3282 && (uData
->access
<= max_access
)
3283 && match_ircglob(uData
->handle
->handle
, mask
))
3284 del_channel_user(uData
, 1);
3287 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
3289 return 1 | override
;
3292 static CHANSERV_FUNC(cmd_mdelowner
)
3294 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
3297 static CHANSERV_FUNC(cmd_mdelcoowner
)
3299 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
3302 static CHANSERV_FUNC(cmd_mdelmanager
)
3304 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
3307 static CHANSERV_FUNC(cmd_mdelop
)
3309 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
3312 static CHANSERV_FUNC(cmd_mdelhalfop
)
3314 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
3317 static CHANSERV_FUNC(cmd_mdelpeon
)
3319 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
3323 static CHANSERV_FUNC(cmd_levels
)
3325 struct helpfile_table tbl
;
3328 tbl
.length
= 6 + 1; // 6 levels
3331 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
3332 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
3333 tbl
.contents
[0][0] = "Level";
3334 tbl
.contents
[0][1] = "From";
3335 tbl
.contents
[0][2] = "-";
3336 tbl
.contents
[0][3] = "To";
3338 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3339 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
3340 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
3341 tbl
.contents
[ii
][2] = msnprintf(2, " ");
3342 tbl
.contents
[ii
][3] = msnprintf(1, "");
3344 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3345 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3346 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3347 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3348 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3350 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3351 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3352 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3353 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3354 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3356 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3357 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3358 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3359 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3360 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3362 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3363 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3364 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3365 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3366 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3368 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3369 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3370 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3371 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3372 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3374 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3378 reply("CSMSG_LEVELS_HEADER");
3379 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3380 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3381 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3382 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3383 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3384 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3391 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3393 struct banData
*bData
, *next
;
3394 char interval
[INTERVALLEN
];
3399 limit
= now
- duration
;
3400 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3404 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3407 del_channel_ban(bData
);
3411 intervalString(interval
, duration
, user
->handle_info
);
3412 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3417 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
)
3419 struct userData
*actor
, *uData
, *next
;
3420 char interval
[INTERVALLEN
];
3424 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3425 actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3427 if(min_access
> max_access
)
3429 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3433 if(!actor
|| actor
->access
<= max_access
)
3435 reply("CSMSG_NO_ACCESS");
3440 limit
= now
- duration
;
3441 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3445 if((uData
->seen
> limit
)
3447 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3450 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3451 || (!max_access
&& (uData
->access
< actor
->access
)))
3453 del_channel_user(uData
, 1);
3461 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3463 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3467 static CHANSERV_FUNC(cmd_trim
)
3469 unsigned long duration
;
3470 unsigned short min_level
, max_level
;
3475 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3476 duration
= ParseInterval(argv
[2]);
3479 reply("CSMSG_CANNOT_TRIM");
3483 if(!irccasecmp(argv
[1], "lamers"))
3485 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3488 else if(!irccasecmp(argv
[1], "users"))
3490 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3493 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3495 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3498 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3500 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3505 reply("CSMSG_INVALID_TRIM", argv
[1]);
3510 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3511 to the user. cmd_all takes advantage of this. */
3512 static CHANSERV_FUNC(cmd_up
)
3514 struct mod_chanmode change
;
3515 struct userData
*uData
;
3518 mod_chanmode_init(&change
);
3520 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3521 if(!change
.args
[0].u
.member
)
3524 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3528 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3532 reply("CSMSG_GODMODE_UP", argv
[0]);
3535 else if(uData
->access
>= UL_OP
)
3537 change
.args
[0].mode
= MODE_CHANOP
;
3538 errmsg
= "CSMSG_ALREADY_OPPED";
3540 else if(uData
->access
>= UL_HALFOP
)
3542 change
.args
[0].mode
= MODE_HALFOP
;
3543 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3545 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3547 change
.args
[0].mode
= MODE_VOICE
;
3548 errmsg
= "CSMSG_ALREADY_VOICED";
3553 reply("CSMSG_NO_ACCESS");
3556 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3557 if(!change
.args
[0].mode
)
3560 reply(errmsg
, channel
->name
);
3563 modcmd_chanmode_announce(&change
);
3567 static CHANSERV_FUNC(cmd_down
)
3569 struct mod_chanmode change
;
3571 mod_chanmode_init(&change
);
3573 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3574 if(!change
.args
[0].u
.member
)
3577 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3581 if(!change
.args
[0].u
.member
->modes
)
3584 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3588 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3589 modcmd_chanmode_announce(&change
);
3593 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
)
3595 struct userData
*cList
;
3597 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3599 if(IsSuspended(cList
->channel
)
3600 || IsUserSuspended(cList
)
3601 || !GetUserMode(cList
->channel
->channel
, user
))
3604 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3610 static CHANSERV_FUNC(cmd_upall
)
3612 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3615 static CHANSERV_FUNC(cmd_downall
)
3617 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3620 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3621 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3624 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
)
3626 unsigned int ii
, valid
;
3627 struct userNode
*victim
;
3628 struct mod_chanmode
*change
;
3630 change
= mod_chanmode_alloc(argc
- 1);
3632 for(ii
=valid
=0; ++ii
< argc
; )
3634 if(!(victim
= GetUserH(argv
[ii
])))
3636 change
->args
[valid
].mode
= mode
;
3637 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3638 if(!change
->args
[valid
].u
.member
)
3640 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3645 change
->argc
= valid
;
3646 if(valid
< (argc
-1))
3647 reply("CSMSG_PROCESS_FAILED");
3650 modcmd_chanmode_announce(change
);
3651 reply(action
, channel
->name
);
3653 mod_chanmode_free(change
);
3657 static CHANSERV_FUNC(cmd_op
)
3659 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3662 static CHANSERV_FUNC(cmd_hop
)
3664 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3667 static CHANSERV_FUNC(cmd_deop
)
3669 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3672 static CHANSERV_FUNC(cmd_dehop
)
3674 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3677 static CHANSERV_FUNC(cmd_voice
)
3679 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3682 static CHANSERV_FUNC(cmd_devoice
)
3684 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3688 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3694 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3696 struct modeNode
*mn
= channel
->members
.list
[ii
];
3698 if(IsService(mn
->user
))
3701 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3704 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3708 victims
[(*victimCount
)++] = mn
;
3714 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3716 struct userNode
*victim
;
3717 struct modeNode
**victims
;
3718 unsigned int offset
, n
, victimCount
, duration
= 0;
3719 char *reason
= "Bye.", *ban
, *name
;
3720 char interval
[INTERVALLEN
];
3722 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3723 REQUIRE_PARAMS(offset
);
3726 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3727 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3729 /* Truncate the reason to a length of TOPICLEN, as
3730 the ircd does; however, leave room for an ellipsis
3731 and the kicker's nick. */
3732 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3736 if((victim
= GetUserH(argv
[1])))
3738 victims
= alloca(sizeof(victims
[0]));
3739 victims
[0] = GetUserMode(channel
, victim
);
3740 /* XXX: The comparison with ACTION_KICK is just because all
3741 * other actions can work on users outside the channel, and we
3742 * want to allow those (e.g. unbans) in that case. If we add
3743 * some other ejection action for in-channel users, change
3745 victimCount
= victims
[0] ? 1 : 0;
3747 if(IsService(victim
))
3750 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3754 if((action
== ACTION_KICK
) && !victimCount
)
3757 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3761 if(protect_user(victim
, user
, channel
->channel_info
, false))
3763 // This translates to send_message(user, cmd->parent->bot, ...)
3764 // if user is x3 (ctcp action) cmd is null and segfault.
3766 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3770 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3771 name
= victim
->nick
;
3773 else if(!is_ircmask(argv
[1]) && (*argv
[1] == '*'))
3775 struct handle_info
*hi
;
3776 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
3777 const char *accountname
= argv
[1] + 1;
3779 if(!(hi
= get_handle_info(accountname
)))
3781 reply("MSG_HANDLE_UNKNOWN", accountname
);
3785 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", hi
->handle
);
3786 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3788 if(bad_channel_ban(channel
, user
, banmask
, &victimCount
, victims
))
3790 reply("CSMSG_MASK_PROTECTED", banmask
);
3794 if((action
== ACTION_KICK
) && (victimCount
== 0))
3796 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, banmask
);
3800 name
= ban
= strdup(banmask
);
3804 if(!is_ircmask(argv
[1]))
3807 reply("MSG_NICK_UNKNOWN", argv
[1]);
3811 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3813 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3816 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3819 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3820 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3822 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3823 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3824 some creativity, but its not x3's job to be the ban censor anyway. */
3825 if(is_overmask(argv
[1]))
3828 reply("CSMSG_LAME_MASK", argv
[1]);
3832 if((action
== ACTION_KICK
) && (victimCount
== 0))
3835 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3839 name
= ban
= strdup(argv
[1]);
3842 /* Truncate the ban in place if necessary; we must ensure
3843 that 'ban' is a valid ban mask before sanitizing it. */
3844 sanitize_ircmask(ban
);
3846 if(action
& ACTION_ADD_LAMER
)
3848 struct banData
*bData
, *next
;
3850 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3853 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3858 if(action
& ACTION_ADD_TIMED_LAMER
)
3860 duration
= ParseInterval(argv
[2]);
3865 reply("CSMSG_DURATION_TOO_LOW");
3869 else if(duration
> (86400 * 365 * 2))
3872 reply("CSMSG_DURATION_TOO_HIGH");
3879 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3881 if(match_ircglobs(bData
->mask
, ban
))
3883 int exact
= !irccasecmp(bData
->mask
, ban
);
3885 /* The ban is redundant; there is already a ban
3886 with the same effect in place. */
3890 free(bData
->reason
);
3891 bData
->reason
= strdup(reason
);
3892 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3894 reply("CSMSG_REASON_CHANGE", ban
);
3898 if(exact
&& bData
->expires
)
3902 /* If the ban matches an existing one exactly,
3903 extend the expiration time if the provided
3904 duration is longer. */
3905 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3907 bData
->expires
= now
+ duration
;
3918 /* Delete the expiration timeq entry and
3919 requeue if necessary. */
3920 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3923 timeq_add(bData
->expires
, expire_ban
, bData
);
3927 /* automated kickban, dont reply */
3930 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3932 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3938 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3945 if(match_ircglobs(ban
, bData
->mask
))
3947 /* The ban we are adding makes previously existing
3948 bans redundant; silently remove them. */
3949 del_channel_ban(bData
);
3953 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
);
3955 name
= ban
= strdup(bData
->mask
);
3959 /* WHAT DOES THIS DO?? -Rubin */
3960 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3962 extern const char *hidden_host_suffix
;
3963 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3965 unsigned int l1
, l2
;
3968 l2
= strlen(old_name
);
3971 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3973 new_mask
= malloc(MAXLEN
);
3974 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3976 name
= ban
= new_mask
;
3981 if(action
& ACTION_BAN
)
3983 unsigned int exists
;
3984 struct mod_chanmode
*change
;
3986 if(channel
->banlist
.used
>= MAXBANS
)
3989 reply("CSMSG_BANLIST_FULL", channel
->name
);
3994 exists
= ChannelBanExists(channel
, ban
);
3995 change
= mod_chanmode_alloc(victimCount
+ 1);
3996 for(n
= 0; n
< victimCount
; ++n
)
3998 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3999 change
->args
[n
].u
.member
= victims
[n
];
4003 change
->args
[n
].mode
= MODE_BAN
;
4004 change
->args
[n
++].u
.hostmask
= ban
;
4008 modcmd_chanmode_announce(change
);
4010 mod_chanmode_announce(chanserv
, channel
, change
);
4011 mod_chanmode_free(change
);
4013 if(exists
&& (action
== ACTION_BAN
))
4016 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
4022 if(action
& ACTION_ADD_LAMER
)
4024 char kick_reason
[MAXLEN
];
4025 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4027 for(n
= 0; n
< victimCount
; n
++) {
4028 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
4029 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4033 else if(action
& ACTION_KICK
)
4035 char kick_reason
[MAXLEN
];
4036 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
4038 for(n
= 0; n
< victimCount
; n
++) {
4039 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
4045 /* No response, since it was automated. */
4047 else if(action
& ACTION_ADD_LAMER
)
4050 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
4052 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
4054 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
4055 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
4056 else if(action
& ACTION_BAN
)
4057 reply("CSMSG_BAN_DONE", name
, channel
->name
);
4058 else if(action
& ACTION_KICK
&& victimCount
)
4059 reply("CSMSG_KICK_DONE", name
, channel
->name
);
4065 static CHANSERV_FUNC(cmd_kickban
)
4067 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
4070 static CHANSERV_FUNC(cmd_kick
)
4072 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
4075 static CHANSERV_FUNC(cmd_ban
)
4077 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
4080 static CHANSERV_FUNC(cmd_addlamer
)
4082 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
4085 static CHANSERV_FUNC(cmd_addtimedlamer
)
4087 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
4090 static struct mod_chanmode
*
4091 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
4093 struct mod_chanmode
*change
;
4094 unsigned char *match
;
4095 unsigned int ii
, count
;
4097 match
= alloca(bans
->used
);
4100 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4102 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
4103 MATCH_USENICK
| MATCH_VISIBLE
);
4110 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4112 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
4119 change
= mod_chanmode_alloc(count
);
4120 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
4124 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
4125 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
4127 assert(count
== change
->argc
);
4131 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
4133 unsigned int jj
, ii
, count
;
4135 struct chanData
*channel
;
4137 struct mod_chanmode
*change
;
4139 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
4140 /* Walk through every channel */
4141 for(channel
= channelList
; channel
; channel
= channel
->next
) {
4142 switch(channel
->chOpts
[chBanTimeout
])
4144 default: case '0': continue; /* Dont remove bans in this chan */
4145 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
4146 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
4147 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
4148 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
4149 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
4152 /* First find out how many bans were going to unset */
4153 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4154 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
4158 /* At least one ban, so setup a removal */
4159 change
= mod_chanmode_alloc(count
);
4161 /* Walk over every ban in this channel.. */
4162 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
4163 bn
= channel
->channel
->banlist
.list
[jj
];
4164 if (bn
->set
< bantimeout
) {
4165 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
4167 /* Add this ban to the mode change */
4168 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4169 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
4171 /* Pull this ban out of the list */
4172 banList_remove(&(channel
->channel
->banlist
), bn
);
4177 /* Send the modes to IRC */
4178 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
4180 /* free memory from strdup above */
4181 for(ii
= 0; ii
< count
; ++ii
)
4182 free((char*)change
->args
[ii
].u
.hostmask
);
4184 mod_chanmode_free(change
);
4187 /* Set this function to run again */
4188 if(chanserv_conf
.ban_timeout_frequency
)
4189 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
4194 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
4196 struct userNode
*actee
;
4202 /* may want to allow a comma delimited list of users... */
4203 if(!(actee
= GetUserH(argv
[1])))
4205 if(!is_ircmask(argv
[1]) && *argv
[1] == '*')
4207 char banmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
4208 const char *accountname
= argv
[1] + 1;
4210 snprintf(banmask
, sizeof(banmask
), "*!*@%s.*", accountname
);
4211 mask
= strdup(banmask
);
4213 else if(!is_ircmask(argv
[1]))
4215 reply("MSG_NICK_UNKNOWN", argv
[1]);
4220 mask
= strdup(argv
[1]);
4224 /* We don't sanitize the mask here because ircu
4226 if(action
& ACTION_UNBAN
)
4228 struct mod_chanmode
*change
;
4229 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
4234 modcmd_chanmode_announce(change
);
4235 for(ii
= 0; ii
< change
->argc
; ++ii
)
4236 free((char*)change
->args
[ii
].u
.hostmask
);
4237 mod_chanmode_free(change
);
4242 if(action
& ACTION_DEL_LAMER
)
4244 struct banData
*ban
, *next
;
4246 ban
= channel
->channel_info
->bans
; /* lamers */
4250 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
4253 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
4258 del_channel_ban(ban
);
4265 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
4267 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
4273 static CHANSERV_FUNC(cmd_unban
)
4275 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
4278 static CHANSERV_FUNC(cmd_dellamer
)
4280 /* it doesn't necessarily have to remove the channel ban - may want
4281 to make that an option. */
4282 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
4285 static CHANSERV_FUNC(cmd_unbanme
)
4287 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4288 long flags
= ACTION_UNBAN
;
4290 /* remove permanent bans if the user has the proper access. */
4291 if(uData
->access
>= UL_MANAGER
)
4292 flags
|= ACTION_DEL_LAMER
;
4294 argv
[1] = user
->nick
;
4295 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
4298 static CHANSERV_FUNC(cmd_unbanall
)
4300 struct mod_chanmode
*change
;
4303 if(!channel
->banlist
.used
)
4305 reply("CSMSG_NO_BANS", channel
->name
);
4309 change
= mod_chanmode_alloc(channel
->banlist
.used
);
4310 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
4312 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
4313 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
4315 modcmd_chanmode_announce(change
);
4316 for(ii
= 0; ii
< change
->argc
; ++ii
)
4317 free((char*)change
->args
[ii
].u
.hostmask
);
4318 mod_chanmode_free(change
);
4319 reply("CSMSG_BANS_REMOVED", channel
->name
);
4323 static CHANSERV_FUNC(cmd_open
)
4325 struct mod_chanmode
*change
;
4328 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
4330 change
= mod_chanmode_alloc(0);
4331 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
4332 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4333 && channel
->channel_info
->modes
.modes_set
)
4334 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
4335 modcmd_chanmode_announce(change
);
4336 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
4337 for(ii
= 0; ii
< change
->argc
; ++ii
)
4338 free((char*)change
->args
[ii
].u
.hostmask
);
4339 mod_chanmode_free(change
);
4343 static CHANSERV_FUNC(cmd_myaccess
)
4345 static struct string_buffer sbuf
;
4346 struct handle_info
*target_handle
;
4347 struct userData
*uData
;
4350 target_handle
= user
->handle_info
;
4351 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
4353 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
4355 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
4358 if(!target_handle
->channels
)
4360 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
4364 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
4365 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
4367 struct chanData
*cData
= uData
->channel
;
4369 if(uData
->access
> UL_OWNER
)
4371 if(IsProtected(cData
)
4372 && (target_handle
!= user
->handle_info
)
4373 && !GetTrueChannelAccess(cData
, user
->handle_info
))
4376 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
4377 if(uData
->flags
== USER_AUTO_OP
)
4378 string_buffer_append(&sbuf
, ',');
4379 if(IsUserSuspended(uData
))
4380 string_buffer_append(&sbuf
, 's');
4381 if(IsUserAutoOp(uData
))
4383 if(uData
->access
>= UL_OP
)
4384 string_buffer_append(&sbuf
, 'o');
4385 else if(uData
->access
>= UL_HALFOP
)
4386 string_buffer_append(&sbuf
, 'h');
4387 else if(uData
->access
>= UL_PEON
)
4388 string_buffer_append(&sbuf
, 'v');
4390 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4391 string_buffer_append(&sbuf
, 'i');
4392 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4393 string_buffer_append(&sbuf
, 'j');
4395 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4397 string_buffer_append_string(&sbuf
, ")]");
4398 string_buffer_append(&sbuf
, '\0');
4399 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4405 static CHANSERV_FUNC(cmd_access
)
4407 struct userNode
*target
;
4408 struct handle_info
*target_handle
;
4409 struct userData
*uData
;
4411 char prefix
[MAXLEN
];
4416 target_handle
= target
->handle_info
;
4418 else if((target
= GetUserH(argv
[1])))
4420 target_handle
= target
->handle_info
;
4422 else if(argv
[1][0] == '*')
4424 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4426 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4432 reply("MSG_NICK_UNKNOWN", argv
[1]);
4436 assert(target
|| target_handle
);
4438 if(target
== chanserv
)
4440 reply("CSMSG_IS_CHANSERV");
4448 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4453 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4456 reply("MSG_AUTHENTICATE");
4462 const char *epithet
= NULL
, *type
= NULL
;
4465 epithet
= chanserv_conf
.irc_operator_epithet
;
4466 type
= user_find_message(user
, "CSMSG_OPERATOR_TITLE");
4468 else if(IsNetworkHelper(target
))
4470 epithet
= chanserv_conf
.network_helper_epithet
;
4471 type
= user_find_message(user
, "CSMSG_UC_H_TITLE");
4473 else if(IsSupportHelper(target
))
4475 epithet
= chanserv_conf
.support_helper_epithet
;
4476 type
= user_find_message(user
, "CSMSG_LC_H_TITLE");
4480 if(target_handle
->epithet
)
4481 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4483 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4485 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4489 sprintf(prefix
, "%s", target_handle
->handle
);
4492 if(!channel
->channel_info
)
4494 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4498 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4499 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4500 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4502 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4503 /* To prevent possible information leaks, only show infolines
4504 * if the requestor is in the channel or it's their own
4506 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4508 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4510 /* Likewise, only say it's suspended if the user has active
4511 * access in that channel or it's their own entry. */
4512 if(IsUserSuspended(uData
)
4513 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4514 || (user
->handle_info
== uData
->handle
)))
4516 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4521 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4527 /* This is never used...
4529 zoot_list(struct listData *list)
4531 struct userData *uData;
4532 unsigned int start, curr, highest, lowest;
4533 struct helpfile_table tmp_table;
4534 const char **temp, *msg;
4536 if(list->table.length == 1)
4539 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);
4541 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));
4542 msg = user_find_message(list->user, "MSG_NONE");
4543 send_message_type(4, list->user, list->bot, " %s", msg);
4545 tmp_table.width = list->table.width;
4546 tmp_table.flags = list->table.flags;
4547 list->table.contents[0][0] = " ";
4548 highest = list->highest;
4549 if(list->lowest != 0)
4550 lowest = list->lowest;
4551 else if(highest < 100)
4554 lowest = highest - 100;
4555 for(start = curr = 1; curr < list->table.length; )
4557 uData = list->users[curr-1];
4558 list->table.contents[curr++][0] = " ";
4559 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4562 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);
4564 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));
4565 temp = list->table.contents[--start];
4566 list->table.contents[start] = list->table.contents[0];
4567 tmp_table.contents = list->table.contents + start;
4568 tmp_table.length = curr - start;
4569 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4570 list->table.contents[start] = temp;
4572 highest = lowest - 1;
4573 lowest = (highest < 100) ? 0 : (highest - 99);
4580 normal_list(struct listData
*list
)
4584 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
);
4586 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
));
4587 if(list
->table
.length
== 1)
4589 msg
= user_find_message(list
->user
, "MSG_NONE");
4590 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4593 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4596 /* if these need changed, uncomment and customize
4598 clean_list(struct listData *list)
4602 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);
4604 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));
4605 if(list->table.length == 1)
4607 msg = user_find_message(list->user, "MSG_NONE");
4608 send_message_type(4, list->user, list->bot, " %s", msg);
4611 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4615 advanced_list(struct listData *list)
4619 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);
4621 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));
4622 if(list->table.length == 1)
4624 msg = user_find_message(list->user, "MSG_NONE");
4625 send_message_type(4, list->user, list->bot, " %s", msg);
4628 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4632 classic_list(struct listData *list)
4636 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4638 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4639 if(list->table.length == 1)
4641 msg = user_find_message(list->user, "MSG_NONE");
4642 send_message_type(4, list->user, list->bot, " %s", msg);
4645 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4650 userData_access_comp(const void *arg_a
, const void *arg_b
)
4652 const struct userData
*a
= *(struct userData
**)arg_a
;
4653 const struct userData
*b
= *(struct userData
**)arg_b
;
4655 if(a
->access
!= b
->access
)
4656 res
= b
->access
- a
->access
;
4658 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4663 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4665 void (*send_list
)(struct listData
*);
4666 struct userData
*uData
;
4667 struct listData lData
;
4668 unsigned int matches
;
4674 lData
.bot
= cmd
->parent
->bot
;
4675 lData
.channel
= channel
;
4676 lData
.lowest
= lowest
;
4677 lData
.highest
= highest
;
4678 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4679 send_list
= normal_list
;
4680 /* What does the following line do exactly?? */
4681 /*(void)zoot_list; ** since it doesn't show user levels */
4684 if(user->handle_info)
4686 switch(user->handle_info->userlist_style)
4688 case HI_STYLE_CLEAN:
4689 send_list = clean_list;
4691 case HI_STYLE_ADVANCED:
4692 send_list = advanced_list;
4694 case HI_STYLE_CLASSIC:
4695 send_list = classic_list;
4697 case HI_STYLE_NORMAL:
4699 send_list = normal_list;
4704 send_list
= normal_list
;
4706 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4708 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4710 if((uData
->access
< lowest
)
4711 || (uData
->access
> highest
)
4712 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4714 lData
.users
[matches
++] = uData
;
4716 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4718 lData
.table
.length
= matches
+1;
4719 lData
.table
.flags
= TABLE_NO_FREE
;
4720 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4722 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4723 lData
.table
.width
= 6; /* with level = 6 */
4725 lData
.table
.width
= 5; /* without = 5 */
4726 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4727 lData
.table
.contents
[0] = ary
;
4728 if(user
->handle_info
) {
4729 switch(user
->handle_info
->userlist_style
) {
4730 case HI_STYLE_CLASSIC
:
4733 case HI_STYLE_ADVANCED
:
4734 ary
[i
++] = "Access";
4737 case HI_STYLE_CLEAN
:
4738 ary
[i
++] = "Access";
4740 case HI_STYLE_NORMAL
:
4742 ary
[i
++] = "Access";
4747 ary
[i
++] = "Access";
4749 ary
[i
++] = "Account";
4750 ary
[i
] = "Last Seen";
4752 ary
[i
++] = "Status";
4753 ary
[i
++] = "Expiry";
4754 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4756 struct userData
*uData
= lData
.users
[matches
-1];
4757 char seen
[INTERVALLEN
];
4760 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4761 lData
.table
.contents
[matches
] = ary
;
4762 if(user
->handle_info
) {
4763 switch(user
->handle_info
->userlist_style
) {
4764 case HI_STYLE_CLASSIC
:
4765 ary
[i
++] = strtab(uData
->access
);
4767 case HI_STYLE_ADVANCED
:
4768 ary
[i
++] = user_level_name_from_level(uData
->access
);
4769 ary
[i
++] = strtab(uData
->access
);
4771 case HI_STYLE_CLEAN
:
4772 ary
[i
++] = user_level_name_from_level(uData
->access
);
4774 case HI_STYLE_NORMAL
:
4776 ary
[i
++] = user_level_name_from_level(uData
->access
);
4781 ary
[i
++] = user_level_name_from_level(uData
->access
);
4783 ary
[i
++] = uData
->handle
->handle
;
4786 else if(!uData
->seen
)
4789 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4790 ary
[i
] = strdup(ary
[i
]);
4792 if(IsUserSuspended(uData
))
4793 ary
[i
++] = "Suspended";
4794 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4795 ary
[i
++] = "Vacation";
4797 ary
[i
++] = "Normal";
4799 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4800 char delay
[INTERVALLEN
];
4803 if (uData
->accessexpiry
> 0) {
4804 diff
= uData
->accessexpiry
- now
;
4805 intervalString(delay
, diff
, user
->handle_info
);
4807 diff
= uData
->clvlexpiry
- now
;
4808 intervalString(delay
, diff
, user
->handle_info
);
4816 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4818 /* Free strdup above */
4819 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4820 free(lData
.table
.contents
[matches
]);
4822 free(lData
.table
.contents
[0]);
4823 free(lData
.table
.contents
);
4827 /* Remove this now that debugging is over? or improve it for
4828 * users? Would it be better tied into USERS somehow? -Rubin */
4829 static CHANSERV_FUNC(cmd_pending
)
4831 struct adduserPending
*ap
;
4832 reply("CSMSG_ADDUSER_PENDING_HEADER");
4833 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4835 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4836 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4837 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4841 static CHANSERV_FUNC(cmd_users
)
4843 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4846 static CHANSERV_FUNC(cmd_wlist
)
4848 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4851 static CHANSERV_FUNC(cmd_clist
)
4853 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4856 static CHANSERV_FUNC(cmd_mlist
)
4858 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4861 static CHANSERV_FUNC(cmd_olist
)
4863 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4866 static CHANSERV_FUNC(cmd_hlist
)
4868 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4871 static CHANSERV_FUNC(cmd_plist
)
4873 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4876 static CHANSERV_FUNC(cmd_lamers
)
4878 struct userNode
*search_u
= NULL
;
4879 struct helpfile_table tbl
;
4880 unsigned int matches
= 0, timed
= 0, search_wilds
= 0, ii
;
4881 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4882 const char *msg_never
, *triggered
, *expires
;
4883 struct banData
*ban
, **bans
; /* lamers */
4887 else if(strchr(search
= argv
[1], '!'))
4890 search_wilds
= search
[strcspn(search
, "?*")];
4892 else if(!(search_u
= GetUserH(search
)))
4893 reply("MSG_NICK_UNKNOWN", search
);
4895 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4896 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4899 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4903 if(!user_matches_glob(search_u
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
4908 if(search_wilds
? !match_ircglobs(search
, ban
->mask
) : !match_ircglob(search
, ban
->mask
))
4911 bans
[matches
++] = ban
;
4916 tbl
.length
= matches
+ 1;
4917 tbl
.width
= 4 + timed
;
4919 tbl
.flags
= TABLE_NO_FREE
;
4920 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4921 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4922 tbl
.contents
[0][0] = "Mask";
4923 tbl
.contents
[0][1] = "Set By";
4924 tbl
.contents
[0][2] = "Triggered";
4927 tbl
.contents
[0][3] = "Expires";
4928 tbl
.contents
[0][4] = "Reason";
4931 tbl
.contents
[0][3] = "Reason";
4934 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4935 /* reply("MSG_NONE"); */
4936 free(tbl
.contents
[0]);
4941 msg_never
= user_find_message(user
, "MSG_NEVER");
4942 for(ii
= 0; ii
< matches
; )
4948 else if(ban
->expires
)
4949 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4951 expires
= msg_never
;
4954 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4956 triggered
= msg_never
;
4958 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4959 tbl
.contents
[ii
][0] = ban
->mask
;
4960 tbl
.contents
[ii
][1] = ban
->owner
;
4961 tbl
.contents
[ii
][2] = strdup(triggered
);
4964 tbl
.contents
[ii
][3] = strdup(expires
);
4965 tbl
.contents
[ii
][4] = ban
->reason
;
4968 tbl
.contents
[ii
][3] = ban
->reason
;
4970 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4971 /* reply("MSG_MATCH_COUNT", matches); */
4972 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4974 free((char*)tbl
.contents
[ii
][2]);
4976 free((char*)tbl
.contents
[ii
][3]);
4977 free(tbl
.contents
[ii
]);
4979 free(tbl
.contents
[0]);
4986 * return + if the user does NOT have the right to set the topic, and
4987 * the topic is changed.
4990 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4992 struct chanData
*cData
= channel
->channel_info
;
4993 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4995 else if(cData
->topic
)
4996 return irccasecmp(new_topic
, cData
->topic
);
5003 * Makes a givin topic fit into a givin topic mask and returns
5006 * topic_mask - the mask to conform to
5007 * topic - the topic to make conform
5008 * new_topic - the pre-allocated char* to put the new topic into
5010 * modifies: new_topic
5013 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
5015 //char *topic_mask = cData->topic_mask;
5017 int pos
=0, starpos
=-1, dpos
=0, len
;
5019 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
5026 strcpy(new_topic
, "");
5029 len
= strlen(topic
);
5030 if((dpos
+ len
) > TOPICLEN
)
5031 len
= TOPICLEN
+ 1 - dpos
;
5032 memcpy(new_topic
+dpos
, topic
, len
);
5036 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
5037 default: new_topic
[dpos
++] = tchar
; break;
5040 if((dpos
> TOPICLEN
) || tchar
)
5042 strcpy(new_topic
, "");
5045 new_topic
[dpos
] = 0;
5049 static CHANSERV_FUNC(cmd_topic
)
5051 struct chanData
*cData
;
5055 #ifdef WITH_PROTOCOL_P10
5059 cData
= channel
->channel_info
;
5064 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
5065 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
5066 reply("CSMSG_TOPIC_SET", cData
->topic
);
5070 reply("CSMSG_NO_TOPIC", channel
->name
);
5074 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5075 /* If they say "!topic *", use an empty topic. */
5076 if((topic
[0] == '*') && (topic
[1] == 0))
5079 if(bad_topic(channel
, user
, topic
))
5081 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5086 /* If there is a topicmask set, and the new topic doesnt match, make it */
5087 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
5089 char *topic_mask
= cData
->topic_mask
;
5090 char new_topic
[TOPICLEN
+1];
5092 /* make a new topic fitting mask */
5093 conform_topic(topic_mask
, topic
, new_topic
);
5096 /* Topic couldnt fit into mask, was too long */
5097 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
5098 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
5101 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
5103 else /* No mask set, just set the topic */
5104 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
5107 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
5109 /* Grab the topic and save it as the default topic. */
5111 cData
->topic
= strdup(channel
->topic
);
5117 static CHANSERV_FUNC(cmd_mode
)
5119 struct userData
*uData
;
5120 struct mod_chanmode
*change
;
5125 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5126 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5130 change
= &channel
->channel_info
->modes
;
5131 if(change
->modes_set
|| change
->modes_clear
) {
5132 modcmd_chanmode_announce(change
);
5133 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
5135 reply("CSMSG_NO_MODES", channel
->name
);
5139 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5141 base_oplevel
= MAXOPLEVEL
;
5142 else if (uData
->access
>= UL_OWNER
)
5145 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
5146 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
5150 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
5154 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
5155 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
5158 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5159 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
5163 modcmd_chanmode_announce(change
);
5164 mod_chanmode_free(change
);
5165 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
5169 static CHANSERV_FUNC(cmd_invite
)
5171 struct userData
*uData
;
5172 struct userNode
*invite
;
5174 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5178 if(!(invite
= GetUserH(argv
[1])))
5180 reply("MSG_NICK_UNKNOWN", argv
[1]);
5187 if(GetUserMode(channel
, invite
))
5189 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
5197 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5198 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
5201 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
5204 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
5206 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
5207 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
5208 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
5214 irc_invite(chanserv
, invite
, channel
);
5216 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
5221 static CHANSERV_FUNC(cmd_inviteme
)
5223 if(GetUserMode(channel
, user
))
5225 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
5228 if(channel
->channel_info
5229 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
5231 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
5234 irc_invite(cmd
->parent
->bot
, user
, channel
);
5239 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
5242 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
5244 /* We display things based on two dimensions:
5245 * - Issue time: present or absent
5246 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5247 * (in order of precedence, so something both expired and revoked
5248 * only counts as revoked)
5250 combo
= (suspended
->issued
? 4 : 0)
5251 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
5253 case 0: /* no issue time, indefinite expiration */
5254 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
5256 case 1: /* no issue time, expires in future */
5257 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
5258 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
5260 case 2: /* no issue time, expired */
5261 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
5262 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
5264 case 3: /* no issue time, revoked */
5265 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
5266 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
5268 case 4: /* issue time set, indefinite expiration */
5269 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5270 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
5272 case 5: /* issue time set, expires in future */
5273 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5274 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
5275 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5277 case 6: /* issue time set, expired */
5278 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5279 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
5280 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5282 case 7: /* issue time set, revoked */
5283 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
5284 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
5285 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
5288 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
5294 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
5297 const char *fmt
= "%a %b %d %H:%M %Y";
5298 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
5300 if(giveownership
->staff_issuer
)
5302 if(giveownership
->reason
)
5303 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
5304 giveownership
->target
, giveownership
->target_access
,
5305 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
5307 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
5308 giveownership
->target
, giveownership
->target_access
,
5309 giveownership
->staff_issuer
, buf
);
5313 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
5318 static CHANSERV_FUNC(cmd_info
)
5320 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
5321 struct userData
*uData
, *owner
;
5322 struct chanData
*cData
;
5323 struct do_not_register
*dnr
;
5328 cData
= channel
->channel_info
;
5329 reply("CSMSG_CHANNEL_INFO", channel
->name
);
5330 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5333 uData
= GetChannelUser(cData
, user
->handle_info
);
5334 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
5336 mod_chanmode_format(&cData
->modes
, modes
);
5337 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
5338 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
5341 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
5345 note
= iter_data(it
);
5346 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5349 padding
= PADLEN
- 1 - strlen(iter_key(it
));
5350 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
5353 reply("CSMSG_CHANNEL_MAX", cData
->max
);
5354 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
5355 if(owner
->access
== UL_OWNER
)
5356 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
5357 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
5358 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
5359 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
5361 privileged
= IsStaff(user
);
5362 /* if(privileged) */
5363 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
5364 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
5365 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
5367 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
5368 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
5370 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
5372 struct suspended
*suspended
;
5373 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
5374 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
5375 show_suspension_info(cmd
, user
, suspended
);
5377 else if(IsSuspended(cData
))
5379 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
5380 show_suspension_info(cmd
, user
, cData
->suspended
);
5382 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
5384 struct giveownership
*giveownership
;
5385 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
5386 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
5387 show_giveownership_info(cmd
, user
, giveownership
);
5389 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5390 reply("CSMSG_CHANNEL_END");
5392 reply("CSMSG_CHANNEL_END_CLEAN");
5396 static CHANSERV_FUNC(cmd_netinfo
)
5398 extern time_t boot_time
;
5399 extern unsigned long burst_length
;
5400 char interval
[INTERVALLEN
];
5402 reply("CSMSG_NETWORK_INFO");
5403 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5404 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5405 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5406 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5407 reply("CSMSG_NETWORK_LAMERS", banCount
);
5408 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5409 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5410 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5415 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5417 struct helpfile_table table
;
5419 struct userNode
*user
;
5424 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5425 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5426 for(nn
=0; nn
<list
->used
; nn
++)
5428 user
= list
->list
[nn
];
5429 if(user
->modes
& skip_flags
)
5433 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5436 nick
= alloca(strlen(user
->nick
)+3);
5437 sprintf(nick
, "(%s)", user
->nick
);
5441 table
.contents
[table
.length
][0] = nick
;
5444 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5447 static CHANSERV_FUNC(cmd_ircops
)
5449 reply("CSMSG_STAFF_OPERS");
5450 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5454 static CHANSERV_FUNC(cmd_helpers
)
5456 reply("CSMSG_STAFF_HELPERS");
5457 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5461 static CHANSERV_FUNC(cmd_staff
)
5463 reply("CSMSG_NETWORK_STAFF");
5464 cmd_ircops(CSFUNC_ARGS
);
5465 cmd_helpers(CSFUNC_ARGS
);
5469 static CHANSERV_FUNC(cmd_peek
)
5471 struct modeNode
*mn
;
5472 char modes
[MODELEN
];
5474 struct helpfile_table table
;
5476 irc_make_chanmode(channel
, modes
);
5478 reply("CSMSG_PEEK_INFO", channel
->name
);
5479 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5481 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5482 reply("CSMSG_PEEK_MODES", modes
);
5483 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5487 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5488 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5489 for(n
= 0; n
< channel
->members
.used
; n
++)
5491 mn
= channel
->members
.list
[n
];
5492 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5494 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5495 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5500 reply("CSMSG_PEEK_OPS");
5501 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5504 reply("CSMSG_PEEK_NO_OPS");
5505 reply("CSMSG_PEEK_END");
5509 static MODCMD_FUNC(cmd_wipeinfo
)
5511 struct handle_info
*victim
;
5512 struct userData
*ud
, *actor
, *real_actor
;
5513 unsigned int override
= 0;
5516 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5517 real_actor
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5518 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5520 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5522 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5525 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5527 reply("MSG_USER_OUTRANKED", victim
->handle
);
5530 if((ud
!= real_actor
) && (!real_actor
|| (ud
->access
>= real_actor
->access
)))
5531 override
= CMD_LOG_OVERRIDE
;
5535 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5536 return 1 | override
;
5540 resync_channel(struct chanNode
*channel
)
5542 struct mod_chanmode
*changes
;
5543 struct chanData
*cData
= channel
->channel_info
;
5544 unsigned int ii
, used
;
5546 /* 6 = worst case -ovh+ovh on everyone */
5547 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5548 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5550 struct modeNode
*mn
= channel
->members
.list
[ii
];
5551 struct userData
*uData
;
5553 if(IsService(mn
->user
))
5557 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5559 /* If the channel is in no-mode mode, de-mode EVERYONE */
5560 if(cData
->chOpts
[chAutomode
] == 'n')
5564 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5565 changes
->args
[used
++].u
.member
= mn
;
5568 else /* Give various userlevels their modes.. */
5570 if(uData
&& uData
->access
>= UL_OP
)
5572 if(!(mn
->modes
& MODE_CHANOP
))
5574 changes
->args
[used
].mode
= MODE_CHANOP
;
5575 changes
->args
[used
++].u
.member
= mn
;
5578 else if(uData
&& uData
->access
>= UL_HALFOP
)
5580 if(mn
->modes
& MODE_CHANOP
)
5582 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5583 changes
->args
[used
++].u
.member
= mn
;
5585 if(!(mn
->modes
& MODE_HALFOP
))
5587 changes
->args
[used
].mode
= MODE_HALFOP
;
5588 changes
->args
[used
++].u
.member
= mn
;
5591 else if(uData
&& uData
->access
>= UL_PEON
)
5593 if(mn
->modes
& MODE_CHANOP
)
5595 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5596 changes
->args
[used
++].u
.member
= mn
;
5598 if(mn
->modes
& MODE_HALFOP
)
5600 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5601 changes
->args
[used
++].u
.member
= mn
;
5603 /* Don't voice peons if were in mode m */
5604 if( cData
->chOpts
[chAutomode
] == 'm')
5606 if(mn
->modes
& MODE_VOICE
)
5608 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5609 changes
->args
[used
++].u
.member
= mn
;
5612 /* otherwise, make user they do have voice */
5613 else if(!(mn
->modes
& MODE_VOICE
))
5615 changes
->args
[used
].mode
= MODE_VOICE
;
5616 changes
->args
[used
++].u
.member
= mn
;
5619 else /* They arnt on the userlist.. */
5621 /* If we voice everyone, but they dont.. */
5622 if(cData
->chOpts
[chAutomode
] == 'v')
5624 /* Remove anything except v */
5625 if(mn
->modes
& ~MODE_VOICE
)
5627 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5628 changes
->args
[used
++].u
.member
= mn
;
5631 if(!(mn
->modes
& MODE_VOICE
))
5633 changes
->args
[used
].mode
= MODE_VOICE
;
5634 changes
->args
[used
++].u
.member
= mn
;
5637 /* If we hop everyone, but they dont.. */
5638 else if(cData
->chOpts
[chAutomode
] == 'h')
5640 /* Remove anything except h */
5641 if(mn
->modes
& ~MODE_HALFOP
)
5643 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5644 changes
->args
[used
++].u
.member
= mn
;
5647 if(!(mn
->modes
& MODE_HALFOP
))
5649 changes
->args
[used
].mode
= MODE_HALFOP
;
5650 changes
->args
[used
++].u
.member
= mn
;
5653 /* If we op everyone, but they dont.. */
5654 else if(cData
->chOpts
[chAutomode
] == 'o')
5656 /* Remove anything except h */
5657 if(mn
->modes
& ~MODE_CHANOP
)
5659 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5660 changes
->args
[used
++].u
.member
= mn
;
5663 if(!(mn
->modes
& MODE_CHANOP
))
5665 changes
->args
[used
].mode
= MODE_CHANOP
;
5666 changes
->args
[used
++].u
.member
= mn
;
5669 /* they have no excuse for having modes, de-everything them */
5674 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5675 changes
->args
[used
++].u
.member
= mn
;
5681 changes
->argc
= used
;
5682 mod_chanmode_announce(chanserv
, channel
, changes
);
5683 mod_chanmode_free(changes
);
5686 static CHANSERV_FUNC(cmd_resync
)
5688 resync_channel(channel
);
5689 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5693 static CHANSERV_FUNC(cmd_seen
)
5695 struct userData
*uData
;
5696 struct handle_info
*handle
;
5697 char seen
[INTERVALLEN
];
5701 if(!irccasecmp(argv
[1], chanserv
->nick
))
5703 reply("CSMSG_IS_CHANSERV");
5707 if(!(handle
= get_handle_info(argv
[1])))
5709 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5713 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5715 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5720 reply("CSMSG_USER_PRESENT", handle
->handle
);
5721 else if(uData
->seen
)
5722 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5724 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5726 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5727 reply("CSMSG_USER_VACATION", handle
->handle
);
5732 static MODCMD_FUNC(cmd_names
)
5734 struct userNode
*targ
;
5735 struct userData
*targData
;
5736 unsigned int ii
, pos
;
5739 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5741 targ
= channel
->members
.list
[ii
]->user
;
5742 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5745 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5748 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5752 if(IsUserSuspended(targData
))
5754 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5757 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5758 reply("CSMSG_END_NAMES", channel
->name
);
5763 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5765 switch(ntype
->visible_type
)
5767 case NOTE_VIS_ALL
: return 1;
5768 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5769 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5774 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5776 struct userData
*uData
;
5778 switch(ntype
->set_access_type
)
5780 case NOTE_SET_CHANNEL_ACCESS
:
5781 if(!user
->handle_info
)
5783 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5785 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5786 case NOTE_SET_CHANNEL_SETTER
:
5787 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5788 case NOTE_SET_PRIVILEGED
: default:
5789 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5793 static CHANSERV_FUNC(cmd_note
)
5795 struct chanData
*cData
;
5797 struct note_type
*ntype
;
5799 cData
= channel
->channel_info
;
5802 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5806 /* If no arguments, show all visible notes for the channel. */
5812 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5814 note
= iter_data(it
);
5815 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5818 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5819 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5822 reply("CSMSG_NOTELIST_END", channel
->name
);
5824 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5826 /* If one argument, show the named note. */
5829 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5830 && note_type_visible_to_user(cData
, note
->type
, user
))
5832 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5834 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5835 && note_type_visible_to_user(NULL
, ntype
, user
))
5837 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5842 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5846 /* Assume they're trying to set a note. */
5850 ntype
= dict_find(note_types
, argv
[1], NULL
);
5853 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5856 else if(note_type_settable_by_user(channel
, ntype
, user
))
5858 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5859 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5860 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5861 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5862 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5864 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5866 /* The note is viewable to staff only, so return 0
5867 to keep the invocation from getting logged (or
5868 regular users can see it in !events). */
5874 reply("CSMSG_NO_ACCESS");
5881 static CHANSERV_FUNC(cmd_delnote
)
5886 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5887 || !note_type_settable_by_user(channel
, note
->type
, user
))
5889 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5892 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5893 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5897 static CHANSERV_FUNC(cmd_last
)
5903 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5905 if(numoflines
< 1 || numoflines
> 200)
5907 reply("CSMSG_LAST_INVALID");
5910 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5914 static CHANSERV_FUNC(cmd_events
)
5916 struct logSearch discrim
;
5917 struct logReport report
;
5918 unsigned int matches
, limit
;
5920 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5921 if(limit
< 1 || limit
> 200)
5924 memset(&discrim
, 0, sizeof(discrim
));
5925 discrim
.masks
.bot
= chanserv
;
5926 discrim
.masks
.channel_name
= channel
->name
;
5928 discrim
.masks
.command
= argv
[2];
5929 discrim
.limit
= limit
;
5930 discrim
.max_time
= INT_MAX
;
5931 discrim
.severities
= 1 << LOG_COMMAND
;
5932 report
.reporter
= chanserv
;
5934 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5935 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5937 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5939 reply("MSG_MATCH_COUNT", matches
);
5941 reply("MSG_NO_MATCHES");
5945 static CHANSERV_FUNC(cmd_say
)
5951 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5952 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5954 else if(GetUserH(argv
[1]))
5957 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5958 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5962 reply("MSG_NOT_TARGET_NAME");
5968 static CHANSERV_FUNC(cmd_emote
)
5974 /* CTCP is so annoying. */
5975 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5976 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5978 else if(GetUserH(argv
[1]))
5980 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5981 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5985 reply("MSG_NOT_TARGET_NAME");
5991 struct channelList
*
5992 chanserv_support_channels(void)
5994 return &chanserv_conf
.support_channels
;
5997 static CHANSERV_FUNC(cmd_expire
)
5999 int channel_count
= registered_channels
;
6000 expire_channels(NULL
);
6001 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
6006 chanserv_expire_suspension(void *data
)
6008 struct suspended
*suspended
= data
;
6009 struct chanNode
*channel
;
6011 if(!suspended
->expires
|| (now
< suspended
->expires
))
6012 suspended
->revoked
= now
;
6013 channel
= suspended
->cData
->channel
;
6014 suspended
->cData
->channel
= channel
;
6015 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
6016 if(!IsOffChannel(suspended
->cData
))
6018 spamserv_cs_suspend(channel
, 0, 0, NULL
);
6019 ss_cs_join_channel(channel
, 1);
6023 static CHANSERV_FUNC(cmd_csuspend
)
6025 struct suspended
*suspended
;
6026 char reason
[MAXLEN
];
6027 time_t expiry
, duration
;
6028 struct userData
*uData
;
6032 if(IsProtected(channel
->channel_info
))
6034 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
6038 if(argv
[1][0] == '!')
6040 else if(IsSuspended(channel
->channel_info
))
6042 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
6043 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
6047 if(!strcmp(argv
[1], "0"))
6049 else if((duration
= ParseInterval(argv
[1])))
6050 expiry
= now
+ duration
;
6053 reply("MSG_INVALID_DURATION", argv
[1]);
6057 unsplit_string(argv
+ 2, argc
- 2, reason
);
6059 suspended
= calloc(1, sizeof(*suspended
));
6060 suspended
->revoked
= 0;
6061 suspended
->issued
= now
;
6062 suspended
->suspender
= strdup(user
->handle_info
->handle
);
6063 suspended
->expires
= expiry
;
6064 suspended
->reason
= strdup(reason
);
6065 suspended
->cData
= channel
->channel_info
;
6066 suspended
->previous
= suspended
->cData
->suspended
;
6067 suspended
->cData
->suspended
= suspended
;
6069 if(suspended
->expires
)
6070 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6072 if(IsSuspended(channel
->channel_info
))
6074 suspended
->previous
->revoked
= now
;
6075 if(suspended
->previous
->expires
)
6076 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
6078 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
6079 channel
->name
, suspended
->suspender
);
6083 /* Mark all users in channel as absent. */
6084 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
6093 /* Mark the channel as suspended, then part. */
6094 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
6095 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
6096 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
6097 reply("CSMSG_SUSPENDED", channel
->name
);
6098 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
6099 channel
->name
, suspended
->suspender
);
6104 static CHANSERV_FUNC(cmd_cunsuspend
)
6106 struct suspended
*suspended
;
6108 if(!IsSuspended(channel
->channel_info
))
6110 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
6114 suspended
= channel
->channel_info
->suspended
;
6116 /* Expire the suspension and join ChanServ to the channel. */
6117 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
6118 chanserv_expire_suspension(suspended
);
6119 reply("CSMSG_UNSUSPENDED", channel
->name
);
6120 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
6121 channel
->name
, user
->handle_info
->handle
);
6125 typedef struct chanservSearch
6133 unsigned long flags
;
6137 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
6140 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
6145 search
= malloc(sizeof(struct chanservSearch
));
6146 memset(search
, 0, sizeof(*search
));
6149 for(i
= 0; i
< argc
; i
++)
6151 /* Assume all criteria require arguments. */
6154 reply("MSG_MISSING_PARAMS", argv
[i
]);
6158 if(!irccasecmp(argv
[i
], "name"))
6159 search
->name
= argv
[++i
];
6160 else if(!irccasecmp(argv
[i
], "registrar"))
6161 search
->registrar
= argv
[++i
];
6162 else if(!irccasecmp(argv
[i
], "unvisited"))
6163 search
->unvisited
= ParseInterval(argv
[++i
]);
6164 else if(!irccasecmp(argv
[i
], "registered"))
6165 search
->registered
= ParseInterval(argv
[++i
]);
6166 else if(!irccasecmp(argv
[i
], "flags"))
6169 if(!irccasecmp(argv
[i
], "nodelete"))
6170 search
->flags
|= CHANNEL_NODELETE
;
6171 else if(!irccasecmp(argv
[i
], "suspended"))
6172 search
->flags
|= CHANNEL_SUSPENDED
;
6173 else if(!irccasecmp(argv
[i
], "unreviewed"))
6174 search
->flags
|= CHANNEL_UNREVIEWED
;
6177 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
6181 else if(!irccasecmp(argv
[i
], "limit"))
6182 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
6185 reply("MSG_INVALID_CRITERIA", argv
[i
]);
6190 if(search
->name
&& !strcmp(search
->name
, "*"))
6192 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
6193 search
->registrar
= 0;
6202 chanserv_channel_match(struct chanData
*channel
, search_t search
)
6204 const char *name
= channel
->channel
->name
;
6205 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
6206 (search
->registrar
&& !channel
->registrar
) ||
6207 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
6208 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
6209 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
6210 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
6217 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
6219 struct chanData
*channel
;
6220 unsigned int matches
= 0;
6222 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
6224 if(!chanserv_channel_match(channel
, search
))
6234 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
6239 search_print(struct chanData
*channel
, void *data
)
6241 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
6244 static CHANSERV_FUNC(cmd_search
)
6247 unsigned int matches
;
6248 channel_search_func action
;
6252 if(!irccasecmp(argv
[1], "count"))
6253 action
= search_count
;
6254 else if(!irccasecmp(argv
[1], "print"))
6255 action
= search_print
;
6258 reply("CSMSG_ACTION_INVALID", argv
[1]);
6262 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
6266 if(action
== search_count
)
6267 search
->limit
= INT_MAX
;
6269 if(action
== search_print
)
6271 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
6272 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6276 matches
= chanserv_channel_search(search
, action
, user
);
6279 reply("MSG_MATCH_COUNT", matches
);
6281 reply("MSG_NO_MATCHES");
6287 static CHANSERV_FUNC(cmd_unvisited
)
6289 struct chanData
*cData
;
6290 time_t interval
= chanserv_conf
.channel_expire_delay
;
6291 char buffer
[INTERVALLEN
];
6292 unsigned int limit
= 25, matches
= 0;
6296 interval
= ParseInterval(argv
[1]);
6298 limit
= atoi(argv
[2]);
6301 intervalString(buffer
, interval
, user
->handle_info
);
6302 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
6304 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
6306 if((now
- cData
->visited
) < interval
)
6309 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
6310 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
6317 static MODCMD_FUNC(chan_opt_unreviewed
)
6319 struct chanData
*cData
= channel
->channel_info
;
6320 int value
= (cData
->flags
& CHANNEL_UNREVIEWED
) ? 1 : 0;
6326 /* The two directions can have different ACLs. */
6327 if(enabled_string(argv
[1]))
6329 else if(disabled_string(argv
[1]))
6333 reply("MSG_INVALID_BINARY", argv
[1]);
6337 if (new_value
!= value
)
6339 struct svccmd
*subcmd
;
6340 char subcmd_name
[32];
6342 snprintf(subcmd_name
, sizeof(subcmd_name
), "%s %s", argv
[0], (new_value
? "on" : "off"));
6343 subcmd
= dict_find(cmd
->parent
->commands
, subcmd_name
, NULL
);
6346 reply("MSG_COMMAND_DISABLED", subcmd_name
);
6349 else if(!svccmd_can_invoke(user
, cmd
->parent
->bot
, subcmd
, channel
, SVCCMD_NOISY
))
6353 cData
->flags
|= CHANNEL_UNREVIEWED
;
6356 free(cData
->registrar
);
6357 cData
->registrar
= strdup(user
->handle_info
->handle
);
6358 cData
->flags
&= ~CHANNEL_UNREVIEWED
;
6365 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_ON"));
6367 reply("CSMSG_SET_UNREVIEWED", user_find_message(user
, "MSG_OFF"));
6371 static MODCMD_FUNC(chan_opt_defaulttopic
)
6377 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6379 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6383 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
6385 free(channel
->channel_info
->topic
);
6386 if(topic
[0] == '*' && topic
[1] == 0)
6388 topic
= channel
->channel_info
->topic
= NULL
;
6392 topic
= channel
->channel_info
->topic
= strdup(topic
);
6393 if(channel
->channel_info
->topic_mask
6394 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
6395 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6397 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
6400 if(channel
->channel_info
->topic
)
6401 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
6403 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
6407 static MODCMD_FUNC(chan_opt_topicmask
)
6411 struct chanData
*cData
= channel
->channel_info
;
6414 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
6416 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
6420 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
6422 if(cData
->topic_mask
)
6423 free(cData
->topic_mask
);
6424 if(mask
[0] == '*' && mask
[1] == 0)
6426 cData
->topic_mask
= 0;
6430 cData
->topic_mask
= strdup(mask
);
6432 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
6433 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
6434 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
6438 if(channel
->channel_info
->topic_mask
)
6439 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
6441 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
6445 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
6449 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
6453 if(greeting
[0] == '*' && greeting
[1] == 0)
6457 unsigned int length
= strlen(greeting
);
6458 if(length
> chanserv_conf
.greeting_length
)
6460 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6463 *data
= strdup(greeting
);
6472 reply(name
, user_find_message(user
, "MSG_NONE"));
6476 static MODCMD_FUNC(chan_opt_greeting
)
6478 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6481 static MODCMD_FUNC(chan_opt_usergreeting
)
6483 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6486 static MODCMD_FUNC(chan_opt_modes
)
6488 struct mod_chanmode
*new_modes
;
6489 char modes
[MODELEN
];
6493 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6494 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6498 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6500 reply("CSMSG_NO_ACCESS");
6503 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6505 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6507 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6509 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6512 else if(new_modes
->argc
> 1)
6514 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6515 mod_chanmode_free(new_modes
);
6520 channel
->channel_info
->modes
= *new_modes
;
6521 modcmd_chanmode_announce(new_modes
);
6522 mod_chanmode_free(new_modes
);
6526 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6528 reply("CSMSG_SET_MODES", modes
);
6530 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6534 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6536 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6538 struct chanData
*cData
= channel
->channel_info
;
6543 /* Set flag according to value. */
6544 if(enabled_string(argv
[1]))
6546 cData
->flags
|= mask
;
6549 else if(disabled_string(argv
[1]))
6551 cData
->flags
&= ~mask
;
6556 reply("MSG_INVALID_BINARY", argv
[1]);
6562 /* Find current option value. */
6563 value
= (cData
->flags
& mask
) ? 1 : 0;
6567 reply(name
, user_find_message(user
, "MSG_ON"));
6569 reply(name
, user_find_message(user
, "MSG_OFF"));
6573 static MODCMD_FUNC(chan_opt_nodelete
)
6575 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6577 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6581 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6584 static MODCMD_FUNC(chan_opt_dynlimit
)
6586 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6589 static MODCMD_FUNC(chan_opt_offchannel
)
6591 struct chanData
*cData
= channel
->channel_info
;
6596 /* Set flag according to value. */
6597 if(enabled_string(argv
[1]))
6599 if(!IsOffChannel(cData
))
6600 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6601 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6604 else if(disabled_string(argv
[1]))
6606 if(IsOffChannel(cData
))
6608 struct mod_chanmode change
;
6609 mod_chanmode_init(&change
);
6611 change
.args
[0].mode
= MODE_CHANOP
;
6612 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6613 mod_chanmode_announce(chanserv
, channel
, &change
);
6615 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6620 reply("MSG_INVALID_BINARY", argv
[1]);
6626 /* Find current option value. */
6627 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6631 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6633 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6637 static MODCMD_FUNC(chan_opt_defaults
)
6639 struct userData
*uData
;
6640 struct chanData
*cData
;
6641 const char *confirm
;
6642 enum levelOption lvlOpt
;
6643 enum charOption chOpt
;
6645 cData
= channel
->channel_info
;
6646 uData
= GetChannelUser(cData
, user
->handle_info
);
6647 if(!uData
|| (uData
->access
< UL_OWNER
))
6649 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6652 confirm
= make_confirmation_string(uData
);
6653 if((argc
< 2) || strcmp(argv
[1], confirm
))
6655 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6658 cData
->flags
= (CHANNEL_DEFAULT_FLAGS
& ~CHANNEL_PRESERVED_FLAGS
)
6659 | (cData
->flags
& CHANNEL_PRESERVED_FLAGS
);
6660 cData
->modes
= chanserv_conf
.default_modes
;
6661 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6662 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6663 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6664 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6665 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6670 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6672 struct chanData
*cData
= channel
->channel_info
;
6673 struct userData
*uData
;
6674 unsigned short value
;
6678 if(!check_user_level(channel
, user
, option
, 1, 1))
6680 reply("CSMSG_CANNOT_SET");
6683 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6684 if(!value
&& strcmp(argv
[1], "0"))
6686 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6689 uData
= GetChannelUser(cData
, user
->handle_info
);
6690 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6692 reply("CSMSG_BAD_SETLEVEL");
6698 /* This test only applies to owners, since non-owners
6699 * trying to set an option to above their level get caught
6700 * by the CSMSG_BAD_SETLEVEL test above.
6702 if(value
> uData
->access
)
6704 reply("CSMSG_BAD_SETTERS");
6711 cData
->lvlOpts
[option
] = value
;
6713 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6717 static MODCMD_FUNC(chan_opt_enfops
)
6719 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6722 static MODCMD_FUNC(chan_opt_enfhalfops
)
6724 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6726 static MODCMD_FUNC(chan_opt_enfmodes
)
6728 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6731 static MODCMD_FUNC(chan_opt_enftopic
)
6733 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6736 static MODCMD_FUNC(chan_opt_pubcmd
)
6738 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6741 static MODCMD_FUNC(chan_opt_setters
)
6743 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6746 static MODCMD_FUNC(chan_opt_userinfo
)
6748 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6751 static MODCMD_FUNC(chan_opt_topicsnarf
)
6753 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6756 static MODCMD_FUNC(chan_opt_inviteme
)
6758 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6761 /* TODO: Make look like this when no args are
6763 * -X3- -------------------------------
6764 * -X3- BanTimeout: Bans are removed:
6765 * -X3- ----- * indicates current -----
6766 * -X3- 0: [*] Never.
6767 * -X3- 1: [ ] After 10 minutes.
6768 * -X3- 2: [ ] After 2 hours.
6769 * -X3- 3: [ ] After 4 hours.
6770 * -X3- 4: [ ] After 24 hours.
6771 * -X3- 5: [ ] After one week.
6772 * -X3- ------------- End -------------
6775 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6777 struct chanData
*cData
= channel
->channel_info
;
6778 int count
= charOptions
[option
].count
, index
;
6782 index
= atoi(argv
[1]);
6784 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6786 reply("CSMSG_INVALID_NUMERIC", index
);
6787 /* Show possible values. */
6788 for(index
= 0; index
< count
; index
++)
6789 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6793 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6797 /* Find current option value. */
6800 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6804 /* Somehow, the option value is corrupt; reset it to the default. */
6805 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6810 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6814 static MODCMD_FUNC(chan_opt_automode
)
6816 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6819 static MODCMD_FUNC(chan_opt_protect
)
6821 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6824 static MODCMD_FUNC(chan_opt_toys
)
6826 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6829 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6831 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6834 static MODCMD_FUNC(chan_opt_bantimeout
)
6836 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6839 static MODCMD_FUNC(chan_opt_topicrefresh
)
6841 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6844 static MODCMD_FUNC(chan_opt_resync
)
6846 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6849 static struct svccmd_list set_shows_list
;
6852 handle_svccmd_unbind(struct svccmd
*target
) {
6854 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6855 if(target
== set_shows_list
.list
[ii
])
6856 set_shows_list
.used
= 0;
6859 static CHANSERV_FUNC(cmd_set
)
6861 struct svccmd
*subcmd
;
6865 /* Check if we need to (re-)initialize set_shows_list. */
6866 if(!set_shows_list
.used
)
6868 if(!set_shows_list
.size
)
6870 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6871 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6873 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6875 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6876 sprintf(buf
, "%s %s", argv
[0], name
);
6877 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6880 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6883 svccmd_list_append(&set_shows_list
, subcmd
);
6889 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6890 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6892 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6894 subcmd
= set_shows_list
.list
[ii
];
6895 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6897 reply("CSMSG_CHANNEL_OPTIONS_END");
6901 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6902 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6905 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6908 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6910 reply("CSMSG_NO_ACCESS");
6917 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6921 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6923 struct userData
*uData
;
6925 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6928 reply("CSMSG_NOT_USER", channel
->name
);
6934 /* Just show current option value. */
6936 else if(enabled_string(argv
[1]))
6938 uData
->flags
|= mask
;
6940 else if(disabled_string(argv
[1]))
6942 uData
->flags
&= ~mask
;
6946 reply("MSG_INVALID_BINARY", argv
[1]);
6950 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6954 static MODCMD_FUNC(user_opt_autoop
)
6956 struct userData
*uData
;
6958 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6961 reply("CSMSG_NOT_USER", channel
->name
);
6964 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6965 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6967 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6970 static MODCMD_FUNC(user_opt_autoinvite
)
6972 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6975 static MODCMD_FUNC(user_opt_autojoin
)
6977 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6980 static MODCMD_FUNC(user_opt_info
)
6982 struct userData
*uData
;
6985 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6989 /* If they got past the command restrictions (which require access)
6990 * but fail this test, we have some fool with security override on.
6992 reply("CSMSG_NOT_USER", channel
->name
);
6999 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
7000 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
7002 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
7005 bp
= strcspn(infoline
, "\001");
7008 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
7013 if(infoline
[0] == '*' && infoline
[1] == 0)
7016 uData
->info
= strdup(infoline
);
7019 reply("CSMSG_USET_INFO", uData
->info
);
7021 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
7025 struct svccmd_list uset_shows_list
;
7027 static CHANSERV_FUNC(cmd_uset
)
7029 struct svccmd
*subcmd
;
7033 /* Check if we need to (re-)initialize uset_shows_list. */
7034 if(!uset_shows_list
.used
)
7038 "AutoOp", "AutoInvite", "AutoJoin", "Info"
7041 if(!uset_shows_list
.size
)
7043 uset_shows_list
.size
= ArrayLength(options
);
7044 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
7046 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
7048 const char *name
= options
[ii
];
7049 sprintf(buf
, "%s %s", argv
[0], name
);
7050 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7053 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
7056 svccmd_list_append(&uset_shows_list
, subcmd
);
7062 /* Do this so options are presented in a consistent order. */
7063 reply("CSMSG_USER_OPTIONS");
7064 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
7065 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
7069 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
7070 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
7073 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
7077 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
7080 static CHANSERV_FUNC(cmd_giveownership
)
7082 struct handle_info
*new_owner_hi
;
7083 struct userData
*new_owner
;
7084 struct userData
*curr_user
;
7085 struct userData
*invoker
;
7086 struct chanData
*cData
= channel
->channel_info
;
7087 struct do_not_register
*dnr
;
7088 const char *confirm
;
7089 struct giveownership
*giveownership
;
7090 unsigned int force
, override
;
7091 unsigned short co_access
, new_owner_old_access
;
7092 char transfer_reason
[MAXLEN
];
7095 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
7096 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
7098 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
7099 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
7100 && (uData
->access
> 500)
7101 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
7102 || uData
->access
< 500));
7105 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
7107 struct userData
*owner
= NULL
;
7108 for(curr_user
= channel
->channel_info
->users
;
7110 curr_user
= curr_user
->next
)
7112 if(curr_user
->access
!= UL_OWNER
)
7116 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
7123 else if(!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
7125 char delay
[INTERVALLEN
];
7126 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
7127 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
7131 reply("CSMSG_NO_OWNER", channel
->name
);
7134 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
7136 if(new_owner_hi
== user
->handle_info
)
7138 reply("CSMSG_NO_TRANSFER_SELF");
7141 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
7146 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_OWNER
- 1, 0, NULL
, 0);
7150 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
7154 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
7156 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
7159 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
7160 if(!IsHelping(user
))
7161 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
7163 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
7167 invoker
= GetChannelUser(cData
, user
->handle_info
);
7168 if(invoker
->access
<= UL_OWNER
)
7170 confirm
= make_confirmation_string(curr_user
);
7171 if((argc
< 3) || strcmp(argv
[2], confirm
))
7173 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi
->handle
, confirm
);
7178 new_owner_old_access
= new_owner
->access
;
7179 if(new_owner
->access
>= UL_COOWNER
)
7180 co_access
= new_owner
->access
;
7182 co_access
= UL_COOWNER
;
7183 new_owner
->access
= UL_OWNER
;
7185 curr_user
->access
= co_access
;
7186 cData
->ownerTransfer
= now
;
7188 giveownership
= calloc(1, sizeof(*giveownership
));
7189 giveownership
->issued
= now
;
7190 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
7191 giveownership
->target
= strdup(new_owner_hi
->handle
);
7192 giveownership
->target_access
= new_owner_old_access
;
7195 if(argc
> (2 + force
))
7197 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
7198 giveownership
->reason
= strdup(transfer_reason
);
7200 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
7203 giveownership
->previous
= channel
->channel_info
->giveownership
;
7204 channel
->channel_info
->giveownership
= giveownership
;
7206 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
7207 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
7208 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
7213 chanserv_expire_user_suspension(void *data
)
7215 struct userData
*target
= data
;
7217 target
->expires
= 0;
7218 target
->flags
&= ~USER_SUSPENDED
;
7221 static CHANSERV_FUNC(cmd_suspend
)
7223 struct handle_info
*hi
;
7224 struct userData
*self
, *real_self
, *target
;
7225 unsigned int override
= 0;
7229 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7230 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7231 real_self
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7232 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7234 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7237 if(target
->access
>= self
->access
)
7239 reply("MSG_USER_OUTRANKED", hi
->handle
);
7242 if(target
->flags
& USER_SUSPENDED
)
7244 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
7249 target
->present
= 0;
7252 if(!strcmp(argv
[2], "0"))
7256 unsigned int duration
;
7257 if(!(duration
= ParseInterval(argv
[2])))
7259 reply("MSG_INVALID_DURATION", argv
[2]);
7262 expiry
= now
+ duration
;
7265 target
->expires
= expiry
;
7268 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
7270 if(!real_self
|| target
->access
>= real_self
->access
)
7271 override
= CMD_LOG_OVERRIDE
;
7272 target
->flags
|= USER_SUSPENDED
;
7273 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
7274 return 1 | override
;
7277 static CHANSERV_FUNC(cmd_unsuspend
)
7279 struct handle_info
*hi
;
7280 struct userData
*self
, *real_self
, *target
;
7281 unsigned int override
= 0;
7284 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
7285 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
7286 real_self
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
7287 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7289 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7292 if(target
->access
>= self
->access
)
7294 reply("MSG_USER_OUTRANKED", hi
->handle
);
7297 if(!(target
->flags
& USER_SUSPENDED
))
7299 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
7302 if(!real_self
|| target
->access
>= real_self
->access
)
7303 override
= CMD_LOG_OVERRIDE
;
7304 target
->flags
&= ~USER_SUSPENDED
;
7305 scan_user_presence(target
, NULL
);
7306 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
7307 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
7308 return 1 | override
;
7311 static MODCMD_FUNC(cmd_deleteme
)
7313 struct handle_info
*hi
;
7314 struct userData
*target
;
7315 const char *confirm_string
;
7316 unsigned short access
;
7319 hi
= user
->handle_info
;
7320 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
7322 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
7325 if(target
->access
== UL_OWNER
)
7327 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
7330 confirm_string
= make_confirmation_string(target
);
7331 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
7333 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
7336 access
= target
->access
;
7337 channel_name
= strdup(channel
->name
);
7338 del_channel_user(target
, 1);
7339 reply("CSMSG_DELETED_YOU", access
, channel_name
);
7345 chanserv_refresh_topics(UNUSED_ARG(void *data
))
7347 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
7348 struct chanData
*cData
;
7351 for(cData
= channelList
; cData
; cData
= cData
->next
)
7353 if(IsSuspended(cData
))
7355 opt
= cData
->chOpts
[chTopicRefresh
];
7358 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
7361 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
7362 cData
->last_refresh
= refresh_num
;
7364 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
7368 chanserv_auto_resync(UNUSED_ARG(void *data
))
7370 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
7371 struct chanData
*cData
;
7374 for(cData
= channelList
; cData
; cData
= cData
->next
)
7376 if(IsSuspended(cData
)) continue;
7377 opt
= cData
->chOpts
[chResync
];
7378 if(opt
== 'n') continue;
7379 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
7380 resync_channel(cData
->channel
);
7381 cData
->last_resync
= refresh_num
;
7383 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
7386 static CHANSERV_FUNC(cmd_unf
)
7390 char response
[MAXLEN
];
7391 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
7392 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7393 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7396 reply("CSMSG_UNF_RESPONSE");
7400 static CHANSERV_FUNC(cmd_ping
)
7404 char response
[MAXLEN
];
7405 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
7406 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7407 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7410 reply("CSMSG_PING_RESPONSE");
7414 static CHANSERV_FUNC(cmd_wut
)
7418 char response
[MAXLEN
];
7419 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
7420 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
7421 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7424 reply("CSMSG_WUT_RESPONSE");
7429 static CHANSERV_FUNC(cmd_8ball
)
7431 unsigned int i
, j
, accum
;
7436 for(i
=1; i
<argc
; i
++)
7437 for(j
=0; argv
[i
][j
]; j
++)
7438 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7439 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7442 char response
[MAXLEN
];
7443 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
7444 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7447 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7451 #else /* Use cool 8ball instead */
7453 void eightball(char *outcome
, int method
, unsigned int seed
)
7457 #define NUMOFCOLORS 18
7458 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
7459 "white", "black", "grey", "brown",
7460 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7461 "fuchsia","turquoise","magenta", "cyan"};
7462 #define NUMOFLOCATIONS 50
7463 char balllocations
[50][55] = {
7464 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7465 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7466 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7467 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7468 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7469 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7470 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7471 "your bra", "your hair", "your bed", "the couch", "the wall",
7472 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7473 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7474 #define NUMOFPREPS 15
7475 char ballpreps
[50][50] = {
7476 "Near", "Somewhere near", "In", "In", "In",
7477 "In", "Hiding in", "Under", "Next to", "Over",
7478 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7479 #define NUMOFNUMS 34
7480 char ballnums
[50][50] = {
7481 "A hundred", "A thousand", "A few", "42",
7482 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7483 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7484 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7486 #define NUMOFMULTS 8
7487 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7490 * 0: normal (Not used in x3)
7497 if (method
== 1) /* A Color */
7501 answer
= (rand() % 12); /* Make sure this is the # of entries */
7504 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
7506 case 1: strcpy(tmp
, "Sort of a light %s color.");
7508 case 2: strcpy(tmp
, "Dark and dreary %s.");
7510 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
7512 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
7514 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
7516 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
7518 case 10: strcpy(tmp
, "Solid %s.");
7520 case 11: strcpy(tmp
, "Transparent %s.");
7522 default: strcpy(outcome
, "An invalid random number was generated.");
7525 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
7528 else if (method
== 2) /* Location */
7530 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
7532 else if (method
== 3) /* Number of ___ */
7534 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
7538 //Debug(DBGWARNING, "Error in 8ball.");
7543 static CHANSERV_FUNC(cmd_8ball
)
7545 char *word1
, *word2
, *word3
;
7546 static char eb
[MAXLEN
];
7547 unsigned int accum
, i
, j
;
7551 for(i
=1; i
<argc
; i
++)
7552 for(j
=0; argv
[i
][j
]; j
++)
7553 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7555 accum
+= time(NULL
)/3600;
7557 word2
= argc
>2?argv
[2]:"";
7558 word3
= argc
>3?argv
[3]:"";
7561 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
7562 eightball(eb
, 1, accum
);
7563 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7564 eightball(eb
, 1, accum
);
7565 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7566 eightball(eb
, 1, accum
);
7567 /*** LOCATION *****/
7572 (strcasecmp(word1
, "where") == 0) &&
7573 (strcasecmp(word2
, "is") == 0)
7577 strcasecmp(word1
, "where's") == 0
7580 eightball(eb
, 2, accum
);
7582 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
7583 eightball(eb
, 3, accum
);
7587 /* Generic 8ball question.. so pull from x3.conf srvx style */
7590 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7593 char response
[MAXLEN
];
7594 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
7595 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7598 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7604 char response
[MAXLEN
];
7605 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
7606 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7609 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
7614 static CHANSERV_FUNC(cmd_d
)
7616 unsigned long sides
, count
, modifier
, ii
, total
;
7617 char response
[MAXLEN
], *sep
;
7621 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
7631 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
7632 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
7636 else if((sep
[0] == '-') && isdigit(sep
[1]))
7637 modifier
= strtoul(sep
, NULL
, 10);
7638 else if((sep
[0] == '+') && isdigit(sep
[1]))
7639 modifier
= strtoul(sep
+1, NULL
, 10);
7646 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
7651 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7654 for(total
= ii
= 0; ii
< count
; ++ii
)
7655 total
+= (rand() % sides
) + 1;
7658 if((count
> 1) || modifier
)
7660 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7661 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7665 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7666 sprintf(response
, fmt
, total
, sides
);
7669 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7671 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7675 static CHANSERV_FUNC(cmd_huggle
)
7677 /* CTCP must be via PRIVMSG, never notice */
7679 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7681 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7685 static CHANSERV_FUNC(cmd_calc
)
7687 char response
[MAXLEN
];
7690 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7693 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7695 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7699 static CHANSERV_FUNC(cmd_reply
)
7703 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7706 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7708 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7713 chanserv_adjust_limit(void *data
)
7715 struct mod_chanmode change
;
7716 struct chanData
*cData
= data
;
7717 struct chanNode
*channel
= cData
->channel
;
7720 if(IsSuspended(cData
))
7723 cData
->limitAdjusted
= now
;
7724 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7725 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7727 if(limit
> cData
->modes
.new_limit
)
7728 limit
= cData
->modes
.new_limit
;
7729 else if(limit
== cData
->modes
.new_limit
)
7733 mod_chanmode_init(&change
);
7734 change
.modes_set
= MODE_LIMIT
;
7735 change
.new_limit
= limit
;
7736 mod_chanmode_announce(chanserv
, channel
, &change
);
7740 handle_new_channel(struct chanNode
*channel
)
7742 struct chanData
*cData
;
7744 if(!(cData
= channel
->channel_info
))
7747 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7748 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7750 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7751 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7755 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
7757 struct banData
*bData
;
7758 struct mod_chanmode
*change
;
7760 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
7765 if (chan
->channel_info
) {
7766 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
7768 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
))
7780 check_bans(struct userNode
*user
, const char *channel
)
7782 struct chanNode
*chan
;
7783 struct mod_chanmode change
;
7784 struct chanData
*cData
;
7785 struct banData
*bData
;
7787 if (!(chan
= GetChannel(channel
)))
7790 if(!(cData
= chan
->channel_info
))
7793 mod_chanmode_init(&change
);
7796 if(chan
->banlist
.used
< MAXBANS
)
7798 /* Not joining through a ban. */
7799 for(bData
= cData
->bans
;
7800 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7801 bData
= bData
->next
);
7805 char kick_reason
[MAXLEN
];
7806 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7808 bData
->triggered
= now
;
7809 if(bData
!= cData
->bans
)
7811 /* Shuffle the ban to the head of the list. */
7813 bData
->next
->prev
= bData
->prev
;
7815 bData
->prev
->next
= bData
->next
;
7818 bData
->next
= cData
->bans
;
7821 cData
->bans
->prev
= bData
;
7823 cData
->bans
= bData
;
7826 change
.args
[0].mode
= MODE_BAN
;
7827 change
.args
[0].u
.hostmask
= bData
->mask
;
7828 mod_chanmode_announce(chanserv
, chan
, &change
);
7829 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
7837 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
7840 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
7842 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
))
7849 /* Welcome to my worst nightmare. Warning: Read (or modify)
7850 the code below at your own risk. */
7852 handle_join(struct modeNode
*mNode
)
7854 struct mod_chanmode change
;
7855 struct userNode
*user
= mNode
->user
;
7856 struct chanNode
*channel
= mNode
->channel
;
7857 struct chanData
*cData
;
7858 struct userData
*uData
= NULL
;
7859 struct banData
*bData
;
7860 struct handle_info
*handle
;
7861 unsigned int modes
= 0, info
= 0;
7864 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7867 cData
= channel
->channel_info
;
7868 if(channel
->members
.used
> cData
->max
)
7869 cData
->max
= channel
->members
.used
;
7872 /* Check for bans. If they're joining through a ban, one of two
7874 * 1: Join during a netburst, by riding the break. Kick them
7875 * unless they have ops or voice in the channel.
7876 * 2: They're allowed to join through the ban (an invite in
7877 * ircu2.10, or a +e on Hybrid, or something).
7878 * If they're not joining through a ban, and the banlist is not
7879 * full, see if they're on the banlist for the channel. If so,
7882 if(user
->uplink
->burst
&& !mNode
->modes
)
7885 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7887 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7889 /* Riding a netburst. Naughty. */
7890 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7897 if(user
->handle_info
)
7899 handle
= user
->handle_info
;
7902 uData
= GetTrueChannelAccess(cData
, handle
);
7907 mod_chanmode_init(&change
);
7910 /* TODO: maybe only people above inviteme level? -Rubin */
7911 /* We don't kick people with access */
7912 if(!uData
&& !channel_user_is_exempt(user
, channel
))
7914 if(channel
->banlist
.used
< MAXBANS
)
7916 /* Not joining through a ban. */
7917 for(bData
= cData
->bans
;
7918 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7919 bData
= bData
->next
);
7923 char kick_reason
[MAXLEN
];
7924 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7926 bData
->triggered
= now
;
7927 if(bData
!= cData
->bans
)
7929 /* Shuffle the ban to the head of the list. */
7931 bData
->next
->prev
= bData
->prev
;
7933 bData
->prev
->next
= bData
->next
;
7936 bData
->next
= cData
->bans
;
7939 cData
->bans
->prev
= bData
;
7940 cData
->bans
= bData
;
7943 change
.args
[0].mode
= MODE_BAN
;
7944 change
.args
[0].u
.hostmask
= bData
->mask
;
7945 mod_chanmode_announce(chanserv
, channel
, &change
);
7946 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7952 /* ChanServ will not modify the limits in join-flooded channels.
7953 It will also skip DynLimit processing when the user (or srvx)
7954 is bursting in, because there are likely more incoming. */
7955 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7956 && !user
->uplink
->burst
7957 && !channel
->join_flooded
7958 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7960 /* The user count has begun "bumping" into the channel limit,
7961 so set a timer to raise the limit a bit. Any previous
7962 timers are removed so three incoming users within the delay
7963 results in one limit change, not three. */
7965 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7966 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7969 /* Give automodes exept during join-floods */
7970 if(!channel
->join_flooded
)
7972 if(cData
->chOpts
[chAutomode
] == 'v')
7973 modes
|= MODE_VOICE
;
7974 else if(cData
->chOpts
[chAutomode
] == 'h')
7975 modes
|= MODE_HALFOP
;
7976 else if(cData
->chOpts
[chAutomode
] == 'o')
7977 modes
|= MODE_CHANOP
;
7980 greeting
= cData
->greeting
;
7981 if(user
->handle_info
)
7983 /* handle = user->handle_info; */
7985 if(IsHelper(user
) && !IsHelping(user
))
7988 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7990 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7992 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7998 /* uData = GetTrueChannelAccess(cData, handle); */
7999 if(uData
&& !IsUserSuspended(uData
))
8001 /* non users getting automodes are handled above. */
8002 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
8004 /* just op everyone with access */
8005 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
8006 modes
|= MODE_VOICE
;
8007 /* or do their access level */
8008 else if(uData
->access
>= UL_OP
)
8009 modes
|= MODE_CHANOP
;
8010 else if(uData
->access
>= UL_HALFOP
)
8011 modes
|= MODE_HALFOP
;
8012 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
8013 modes
|= MODE_VOICE
;
8015 if(uData
->access
>= UL_PRESENT
)
8016 cData
->visited
= now
;
8017 if(cData
->user_greeting
)
8018 greeting
= cData
->user_greeting
;
8020 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
8021 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
8029 /* If user joining normally (not during burst), apply op or voice,
8030 * and send greeting/userinfo as appropriate.
8032 if(!user
->uplink
->burst
)
8036 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8037 if(modes & MODE_CHANOP) {
8038 modes &= ~MODE_HALFOP;
8039 modes &= ~MODE_VOICE;
8042 change
.args
[0].mode
= modes
;
8043 change
.args
[0].u
.member
= mNode
;
8044 mod_chanmode_announce(chanserv
, channel
, &change
);
8047 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
8049 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
8055 chanserv_autojoin_channels(struct userNode
*user
)
8057 struct userData
*channel
;
8059 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8061 struct chanNode
*cn
;
8062 struct modeNode
*mn
;
8064 if(IsUserSuspended(channel
)
8065 || IsSuspended(channel
->channel
)
8066 || !(cn
= channel
->channel
->channel
))
8069 mn
= GetUserMode(cn
, user
);
8072 if(!IsUserSuspended(channel
)
8073 && IsUserAutoJoin(channel
)
8074 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8076 && !user
->uplink
->burst
)
8077 irc_svsjoin(chanserv
, user
, cn
);
8083 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
8085 struct mod_chanmode change
;
8086 struct userData
*channel
;
8087 unsigned int ii
, jj
, i
;
8089 if(!user
->handle_info
)
8092 mod_chanmode_init(&change
);
8094 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
8096 struct chanNode
*cn
;
8097 struct chanData
*cData
;
8098 struct modeNode
*mn
;
8099 if(IsUserSuspended(channel
)
8100 || IsSuspended(channel
->channel
)
8101 || !(cn
= channel
->channel
->channel
))
8104 cData
= cn
->channel_info
;
8105 mn
= GetUserMode(cn
, user
);
8108 if(!IsUserSuspended(channel
)
8109 && IsUserAutoInvite(channel
)
8110 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
8112 && !user
->uplink
->burst
)
8113 irc_invite(chanserv
, user
, cn
);
8117 if(channel
->access
>= UL_PRESENT
)
8118 channel
->channel
->visited
= now
;
8120 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
8122 if(channel
->access
>= UL_OP
)
8123 change
.args
[0].mode
= MODE_CHANOP
;
8124 else if(channel
->access
>= UL_HALFOP
)
8125 change
.args
[0].mode
= MODE_HALFOP
;
8126 else if(channel
->access
>= UL_PEON
)
8127 change
.args
[0].mode
= MODE_VOICE
;
8129 change
.args
[0].mode
= 0;
8130 change
.args
[0].u
.member
= mn
;
8131 if(change
.args
[0].mode
)
8132 mod_chanmode_announce(chanserv
, cn
, &change
);
8135 channel
->seen
= now
;
8136 channel
->present
= 1;
8139 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8141 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
8142 struct banData
*ban
;
8144 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8145 || !channel
->channel_info
8146 || IsSuspended(channel
->channel_info
))
8148 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8150 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8151 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8153 if(jj
< channel
->banlist
.used
)
8155 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
8157 char kick_reason
[MAXLEN
];
8158 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
8160 change
.args
[0].mode
= MODE_BAN
;
8161 change
.args
[0].u
.hostmask
= ban
->mask
;
8162 mod_chanmode_announce(chanserv
, channel
, &change
);
8163 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
8164 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8165 ban
->triggered
= now
;
8170 if(IsSupportHelper(user
))
8172 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8174 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
8176 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
8182 if (user
->handle_info
->ignores
->used
) {
8183 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
8184 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
8188 if (user
->handle_info
->epithet
)
8189 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
8191 /* process autojoin channels 5 seconds later as this sometimes
8192 happens before autohide */
8193 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8194 chanserv_autojoin_channels(user
);
8198 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
8200 struct chanData
*cData
;
8201 struct userData
*uData
;
8203 cData
= mn
->channel
->channel_info
;
8204 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
8207 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
8209 /* Allow for a bit of padding so that the limit doesn't
8210 track the user count exactly, which could get annoying. */
8211 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
8213 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8214 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8218 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
8220 scan_user_presence(uData
, mn
->user
);
8222 if (uData
->access
>= UL_PRESENT
)
8223 cData
->visited
= now
;
8226 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
8228 unsigned int ii
, jj
;
8229 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8231 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
8232 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
8234 if(jj
< mn
->user
->channels
.used
)
8237 if(ii
== chanserv_conf
.support_channels
.used
)
8238 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
8243 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
8245 struct userData
*uData
;
8247 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
8248 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
8249 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
8252 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
8254 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
8255 KickChannelUser(kicker
, channel
, chanserv
, reason
);
8258 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
8263 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
8265 struct chanData
*cData
;
8267 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
8270 cData
= channel
->channel_info
;
8271 if(bad_topic(channel
, user
, channel
->topic
))
8272 { /* User doesnt have privs to set topics. Undo it */
8273 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
8274 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
8277 /* If there is a topic mask set, and the new topic doesnt match,
8278 * set the topic to mask + new_topic */
8279 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
8281 char new_topic
[TOPICLEN
+1];
8282 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
8285 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
8286 /* and fall through to topicsnarf code below.. */
8288 else /* Topic couldnt fit into mask, was too long */
8290 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
8291 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
8292 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
8296 /* With topicsnarf, grab the topic and save it as the default topic. */
8297 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
8300 cData
->topic
= strdup(channel
->topic
);
8306 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
8308 struct mod_chanmode
*bounce
= NULL
;
8309 unsigned int bnc
, ii
;
8312 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
8315 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
8316 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
8318 char correct
[MAXLEN
];
8319 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
8320 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
8321 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
8323 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
8325 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
8327 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8328 if(!protect_user(victim
, user
, channel
->channel_info
, false))
8331 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8334 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8335 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
8336 if(bounce
->args
[bnc
].u
.member
)
8340 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
8341 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8343 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
8345 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
8347 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
8348 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
8351 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8352 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
8353 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
8356 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
8358 const char *ban
= change
->args
[ii
].u
.hostmask
;
8359 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
8362 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
8363 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
8364 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
8366 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
8371 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
8372 mod_chanmode_announce(chanserv
, channel
, bounce
);
8373 for(ii
= 0; ii
< change
->argc
; ++ii
)
8374 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
8375 free((char*)bounce
->args
[ii
].u
.hostmask
);
8376 mod_chanmode_free(bounce
);
8381 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
8383 struct chanNode
*channel
;
8384 struct banData
*bData
;
8385 struct mod_chanmode change
;
8386 unsigned int ii
, jj
;
8387 char kick_reason
[MAXLEN
];
8389 mod_chanmode_init(&change
);
8391 change
.args
[0].mode
= MODE_BAN
;
8392 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
8394 channel
= user
->channels
.list
[ii
]->channel
;
8395 /* Need not check for bans if they're opped or voiced. */
8396 /* TODO: does this make sense in automode v, h, and o? *
8397 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8398 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
8400 /* Need not check for bans unless channel registration is active. */
8401 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
8403 /* Look for a matching ban already on the channel. */
8404 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
8405 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
8407 /* Need not act if we found one. */
8408 if(jj
< channel
->banlist
.used
)
8410 /* don't kick someone on the userlist */
8411 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
8413 /* Look for a matching ban in this channel. */
8414 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
8416 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
8418 change
.args
[0].u
.hostmask
= bData
->mask
;
8419 mod_chanmode_announce(chanserv
, channel
, &change
);
8420 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
8421 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
8422 bData
->triggered
= now
;
8423 break; /* we don't need to check any more bans in the channel */
8428 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
8430 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
8434 dict_remove2(handle_dnrs
, old_handle
, 1);
8435 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
8436 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
8441 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
8443 struct userNode
*h_user
;
8445 if(handle
->channels
)
8447 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
8448 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
8450 while(handle
->channels
)
8451 del_channel_user(handle
->channels
, 1);
8456 handle_server_link(UNUSED_ARG(struct server
*server
))
8458 struct chanData
*cData
;
8460 for(cData
= channelList
; cData
; cData
= cData
->next
)
8462 if(!IsSuspended(cData
))
8463 cData
->may_opchan
= 1;
8464 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
8465 && !cData
->channel
->join_flooded
8466 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
8467 < chanserv_conf
.adjust_threshold
))
8469 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
8470 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8476 chanserv_conf_read(void)
8480 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
8481 struct mod_chanmode
*change
;
8482 struct string_list
*strlist
;
8483 struct chanNode
*chan
;
8486 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
8488 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
8491 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8492 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8493 chanserv_conf
.support_channels
.used
= 0;
8494 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
8496 for(ii
= 0; ii
< strlist
->used
; ++ii
)
8498 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8501 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
8503 channelList_append(&chanserv_conf
.support_channels
, chan
);
8506 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
8509 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8512 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
8514 channelList_append(&chanserv_conf
.support_channels
, chan
);
8516 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
8517 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
8518 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
8519 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
8520 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
8521 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
8522 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
8523 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
8524 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
8525 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
8526 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
8527 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
8528 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
8529 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
8530 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
8531 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
8532 str
= database_get_data(conf_node
, KEY_DNR_EXPIRE_FREQ
, RECDB_QSTRING
);
8533 chanserv_conf
.dnr_expire_frequency
= str
? ParseInterval(str
) : 3600;
8534 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
8535 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
8536 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
8537 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
8538 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
8539 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
8540 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
8541 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
8542 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
8544 NickChange(chanserv
, str
, 0);
8545 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
8546 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
8547 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
8548 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
8549 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
8550 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
8551 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
8552 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
8553 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
8554 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
8555 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
8556 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
8557 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
8558 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
8559 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
8560 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
8561 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
8562 god_timeout
= str
? ParseInterval(str
) : 60*15;
8563 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
8566 safestrncpy(mode_line
, str
, sizeof(mode_line
));
8567 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
8568 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
8569 && (change
->argc
< 2))
8571 chanserv_conf
.default_modes
= *change
;
8572 mod_chanmode_free(change
);
8574 free_string_list(chanserv_conf
.set_shows
);
8575 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
8577 strlist
= string_list_copy(strlist
);
8580 static const char *list
[] = {
8581 /* free form text */
8582 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8583 /* options based on user level */
8584 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8585 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
8586 /* multiple choice options */
8587 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
8588 /* binary options */
8589 "DynLimit", "NoDelete", "BanTimeout",
8594 strlist
= alloc_string_list(ArrayLength(list
)-1);
8595 for(ii
=0; list
[ii
]; ii
++)
8596 string_list_append(strlist
, strdup(list
[ii
]));
8598 chanserv_conf
.set_shows
= strlist
;
8599 /* We don't look things up now, in case the list refers to options
8600 * defined by modules initialized after this point. Just mark the
8601 * function list as invalid, so it will be initialized.
8603 set_shows_list
.used
= 0;
8604 free_string_list(chanserv_conf
.eightball
);
8605 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
8608 strlist
= string_list_copy(strlist
);
8612 strlist
= alloc_string_list(4);
8613 string_list_append(strlist
, strdup("Yes."));
8614 string_list_append(strlist
, strdup("No."));
8615 string_list_append(strlist
, strdup("Maybe so."));
8617 chanserv_conf
.eightball
= strlist
;
8618 free_string_list(chanserv_conf
.old_ban_names
);
8619 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
8621 strlist
= string_list_copy(strlist
);
8623 strlist
= alloc_string_list(2);
8624 chanserv_conf
.old_ban_names
= strlist
;
8625 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
8626 off_channel
= str
? atoi(str
) : 0;
8630 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
8633 struct note_type
*ntype
;
8636 if(!(obj
= GET_RECORD_OBJECT(rd
)))
8638 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
8641 if(!(ntype
= chanserv_create_note_type(key
)))
8643 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
8647 /* Figure out set access */
8648 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
8650 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8651 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
8653 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
8655 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
8656 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
8658 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
8660 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
8664 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
8665 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8666 ntype
->set_access
.min_opserv
= 0;
8669 /* Figure out visibility */
8670 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
8671 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8672 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
8673 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8674 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
8675 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
8676 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
8677 ntype
->visible_type
= NOTE_VIS_ALL
;
8679 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8681 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
8682 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
8686 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8688 struct handle_info
*handle
;
8689 struct userData
*uData
;
8690 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
8692 unsigned short access
, lastaccess
= 0;
8694 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8696 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
8700 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
8701 if(access
> UL_OWNER
)
8703 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
8707 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
8708 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
8709 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
8710 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
8711 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8712 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
8713 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
8714 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
8715 lastaccess
= lstacc
? atoi(lstacc
) : 0;
8717 handle
= get_handle_info(key
);
8720 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
8724 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
, 0);
8725 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
8726 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
8728 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
8729 if (uData
->accessexpiry
> 0)
8730 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
8732 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
8733 if (uData
->clvlexpiry
> 0)
8734 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
8736 uData
->lastaccess
= lastaccess
;
8738 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
8740 if(uData
->expires
> now
)
8741 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
8743 uData
->flags
&= ~USER_SUSPENDED
;
8746 /* Upgrade: set autoop to the inverse of noautoop */
8747 if(chanserv_read_version
< 2)
8749 /* if noautoop is true, set autoop false, and vice versa */
8750 if(uData
->flags
& USER_NOAUTO_OP
)
8751 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
8753 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
8754 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
);
8760 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8762 struct banData
*bData
;
8763 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
8764 time_t set_time
, triggered_time
, expires_time
;
8766 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8768 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8772 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8773 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8774 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8775 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8776 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8777 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8778 if (!reason
|| !owner
)
8781 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8782 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8784 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8786 expires_time
= set_time
+ atoi(s_duration
);
8790 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8793 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8796 static struct suspended
*
8797 chanserv_read_suspended(dict_t obj
)
8799 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8803 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8804 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8805 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8806 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8807 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8808 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8809 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8810 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8811 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8812 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8816 static struct giveownership
*
8817 chanserv_read_giveownership(dict_t obj
)
8819 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8823 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8824 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8826 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8828 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8829 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8831 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8832 giveownership
->reason
= str
? strdup(str
) : NULL
;
8833 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8834 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8836 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8837 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8838 return giveownership
;
8842 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8844 struct suspended
*suspended
;
8845 struct giveownership
*giveownership
;
8846 struct mod_chanmode
*modes
;
8847 struct chanNode
*cNode
;
8848 struct chanData
*cData
;
8849 struct dict
*channel
, *obj
;
8850 char *str
, *argv
[10];
8854 channel
= hir
->d
.object
;
8856 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8859 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8862 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8865 cData
= register_channel(cNode
, str
);
8868 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8872 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8874 enum levelOption lvlOpt
;
8875 enum charOption chOpt
;
8877 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8878 cData
->flags
= atoi(str
);
8880 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8882 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8884 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8885 else if(levelOptions
[lvlOpt
].old_flag
)
8887 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8888 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8890 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8894 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8896 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8898 cData
->chOpts
[chOpt
] = str
[0];
8901 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8903 enum levelOption lvlOpt
;
8904 enum charOption chOpt
;
8907 cData
->flags
= base64toint(str
, 5);
8908 count
= strlen(str
+= 5);
8909 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8912 if(levelOptions
[lvlOpt
].old_flag
)
8914 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8915 lvl
= levelOptions
[lvlOpt
].flag_value
;
8917 lvl
= levelOptions
[lvlOpt
].default_value
;
8919 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8921 case 'c': lvl
= UL_COOWNER
; break;
8922 case 'm': lvl
= UL_MANAGER
; break;
8923 case 'n': lvl
= UL_OWNER
+1; break;
8924 case 'o': lvl
= UL_OP
; break;
8925 case 'p': lvl
= UL_PEON
; break;
8926 case 'h': lvl
= UL_HALFOP
; break;
8927 case 'w': lvl
= UL_OWNER
; break;
8928 default: lvl
= 0; break;
8930 cData
->lvlOpts
[lvlOpt
] = lvl
;
8932 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8933 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8936 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8938 suspended
= chanserv_read_suspended(obj
);
8939 cData
->suspended
= suspended
;
8940 suspended
->cData
= cData
;
8941 /* We could use suspended->expires and suspended->revoked to
8942 * set the CHANNEL_SUSPENDED flag, but we don't. */
8944 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8946 suspended
= calloc(1, sizeof(*suspended
));
8947 suspended
->issued
= 0;
8948 suspended
->revoked
= 0;
8949 suspended
->suspender
= strdup(str
);
8950 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8951 suspended
->expires
= str
? atoi(str
) : 0;
8952 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8953 suspended
->reason
= strdup(str
? str
: "No reason");
8954 suspended
->previous
= NULL
;
8955 cData
->suspended
= suspended
;
8956 suspended
->cData
= cData
;
8960 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8961 suspended
= NULL
; /* to squelch a warning */
8964 if(IsSuspended(cData
)) {
8965 if(suspended
->expires
> now
)
8966 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8967 else if(suspended
->expires
)
8968 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8971 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8973 giveownership
= chanserv_read_giveownership(obj
);
8974 cData
->giveownership
= giveownership
;
8977 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8978 struct mod_chanmode change
;
8979 mod_chanmode_init(&change
);
8981 change
.args
[0].mode
= MODE_CHANOP
;
8982 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8983 mod_chanmode_announce(chanserv
, cNode
, &change
);
8986 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8987 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8988 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8989 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8990 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8991 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8992 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8993 cData
->max
= str
? atoi(str
) : 0;
8994 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8995 cData
->greeting
= str
? strdup(str
) : NULL
;
8996 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8997 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8998 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8999 cData
->topic_mask
= str
? strdup(str
) : NULL
;
9000 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
9001 cData
->topic
= str
? strdup(str
) : NULL
;
9003 if(!IsSuspended(cData
)
9004 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
9005 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
9006 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
9007 cData
->modes
= *modes
;
9009 cData
->modes
.modes_set
|= MODE_REGISTERED
;
9010 if(cData
->modes
.argc
> 1)
9011 cData
->modes
.argc
= 1;
9012 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
9013 mod_chanmode_free(modes
);
9016 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
9017 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9018 user_read_helper(iter_key(it
), iter_data(it
), cData
);
9020 if(!cData
->users
&& !IsProtected(cData
))
9022 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
9023 unregister_channel(cData
, "has empty user list.");
9027 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
9028 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9029 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
9031 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
9032 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
9034 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
9035 struct record_data
*rd
= iter_data(it
);
9036 const char *note
, *setter
;
9038 if(rd
->type
!= RECDB_OBJECT
)
9040 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
9044 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
9046 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
9048 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
9052 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
9053 if(!setter
) setter
= "<unknown>";
9054 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
9062 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
9064 const char *setter
, *reason
, *str
;
9065 struct do_not_register
*dnr
;
9068 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
9071 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
9074 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
9077 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
9080 str
= database_get_data(hir
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
9081 expiry
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
9082 if(expiry
&& expiry
<= now
)
9084 dnr
= chanserv_add_dnr(key
, setter
, expiry
, reason
);
9087 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
9089 dnr
->set
= atoi(str
);
9095 chanserv_version_read(struct dict
*section
)
9099 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
9101 chanserv_read_version
= atoi(str
);
9102 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
9106 chanserv_saxdb_read(struct dict
*database
)
9108 struct dict
*section
;
9111 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
9112 chanserv_version_read(section
);
9114 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
9115 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9116 chanserv_note_type_read(iter_key(it
), iter_data(it
));
9118 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
9119 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9120 chanserv_channel_read(iter_key(it
), iter_data(it
));
9122 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
9123 for(it
= dict_first(section
); it
; it
= iter_next(it
))
9124 chanserv_dnr_read(iter_key(it
), iter_data(it
));
9130 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
9132 int high_present
= 0;
9133 saxdb_start_record(ctx
, KEY_USERS
, 1);
9134 for(; uData
; uData
= uData
->next
)
9136 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
9138 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
9139 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
9140 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
9141 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
9142 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
9143 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
9145 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
9147 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
9149 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
9150 saxdb_end_record(ctx
);
9152 saxdb_end_record(ctx
);
9153 return high_present
;
9157 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
9161 saxdb_start_record(ctx
, KEY_BANS
, 1);
9162 for(; bData
; bData
= bData
->next
)
9164 saxdb_start_record(ctx
, bData
->mask
, 0);
9165 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
9166 if(bData
->triggered
)
9167 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
9169 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
9171 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
9173 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
9174 saxdb_end_record(ctx
);
9176 saxdb_end_record(ctx
);
9180 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
9182 saxdb_start_record(ctx
, name
, 0);
9183 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
9184 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
9186 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
9188 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
9190 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
9192 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
9193 saxdb_end_record(ctx
);
9197 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
9199 saxdb_start_record(ctx
, name
, 0);
9200 if(giveownership
->staff_issuer
)
9201 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
9202 if(giveownership
->old_owner
)
9203 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
9204 if(giveownership
->target
)
9205 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
9206 if(giveownership
->target_access
)
9207 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
9208 if(giveownership
->reason
)
9209 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
9210 if(giveownership
->issued
)
9211 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
9212 if(giveownership
->previous
)
9213 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
9214 saxdb_end_record(ctx
);
9218 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
9222 enum levelOption lvlOpt
;
9223 enum charOption chOpt
;
9225 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
9227 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
9228 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
9230 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
9231 if(channel
->registrar
)
9232 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
9233 if(channel
->greeting
)
9234 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
9235 if(channel
->user_greeting
)
9236 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
9237 if(channel
->topic_mask
)
9238 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
9239 if(channel
->suspended
)
9240 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
9241 if(channel
->giveownership
)
9242 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
9244 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
9245 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
9246 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
9247 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
9248 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
9250 buf
[0] = channel
->chOpts
[chOpt
];
9252 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
9254 saxdb_end_record(ctx
);
9256 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
9258 mod_chanmode_format(&channel
->modes
, buf
);
9259 saxdb_write_string(ctx
, KEY_MODES
, buf
);
9262 high_present
= chanserv_write_users(ctx
, channel
->users
);
9263 chanserv_write_bans(ctx
, channel
->bans
);
9265 if(dict_size(channel
->notes
))
9269 saxdb_start_record(ctx
, KEY_NOTES
, 1);
9270 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
9272 struct note
*note
= iter_data(it
);
9273 saxdb_start_record(ctx
, iter_key(it
), 0);
9274 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
9275 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
9276 saxdb_end_record(ctx
);
9278 saxdb_end_record(ctx
);
9281 if(channel
->ownerTransfer
)
9282 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
9283 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
9284 saxdb_end_record(ctx
);
9288 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
9292 saxdb_start_record(ctx
, ntype
->name
, 0);
9293 switch(ntype
->set_access_type
)
9295 case NOTE_SET_CHANNEL_ACCESS
:
9296 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
9298 case NOTE_SET_CHANNEL_SETTER
:
9299 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
9301 case NOTE_SET_PRIVILEGED
: default:
9302 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
9305 switch(ntype
->visible_type
)
9307 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
9308 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
9309 case NOTE_VIS_PRIVILEGED
:
9310 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
9312 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
9313 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
9314 saxdb_end_record(ctx
);
9318 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
9320 struct do_not_register
*dnr
;
9321 dict_iterator_t it
, next
;
9323 for(it
= dict_first(dnrs
); it
; it
= next
)
9325 next
= iter_next(it
);
9326 dnr
= iter_data(it
);
9327 if(dnr
->expires
&& dnr
->expires
<= now
)
9329 dict_remove(dnrs
, iter_key(it
));
9332 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
9334 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
9336 saxdb_write_int(ctx
, KEY_EXPIRES
, dnr
->expires
);
9337 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
9338 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
9339 saxdb_end_record(ctx
);
9344 chanserv_saxdb_write(struct saxdb_context
*ctx
)
9347 struct chanData
*channel
;
9349 /* Version Control*/
9350 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
9351 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
9352 saxdb_end_record(ctx
);
9355 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
9356 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
9357 chanserv_write_note_type(ctx
, iter_data(it
));
9358 saxdb_end_record(ctx
);
9361 saxdb_start_record(ctx
, KEY_DNR
, 1);
9362 write_dnrs_helper(ctx
, handle_dnrs
);
9363 write_dnrs_helper(ctx
, plain_dnrs
);
9364 write_dnrs_helper(ctx
, mask_dnrs
);
9365 saxdb_end_record(ctx
);
9368 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
9369 for(channel
= channelList
; channel
; channel
= channel
->next
)
9370 chanserv_write_channel(ctx
, channel
);
9371 saxdb_end_record(ctx
);
9377 chanserv_db_cleanup(void) {
9379 unreg_part_func(handle_part
);
9381 unregister_channel(channelList
, "terminating.");
9382 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
9383 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
9384 free(chanserv_conf
.support_channels
.list
);
9385 dict_delete(handle_dnrs
);
9386 dict_delete(plain_dnrs
);
9387 dict_delete(mask_dnrs
);
9388 dict_delete(note_types
);
9389 free_string_list(chanserv_conf
.eightball
);
9390 free_string_list(chanserv_conf
.old_ban_names
);
9391 free_string_list(chanserv_conf
.set_shows
);
9392 free(set_shows_list
.list
);
9393 free(uset_shows_list
.list
);
9396 struct userData
*helper
= helperList
;
9397 helperList
= helperList
->next
;
9402 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
9403 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9404 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9407 init_chanserv(const char *nick
)
9409 struct chanNode
*chan
;
9411 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
9412 conf_register_reload(chanserv_conf_read
);
9416 reg_server_link_func(handle_server_link
);
9417 reg_new_channel_func(handle_new_channel
);
9418 reg_join_func(handle_join
);
9419 reg_part_func(handle_part
);
9420 reg_kick_func(handle_kick
);
9421 reg_topic_func(handle_topic
);
9422 reg_mode_change_func(handle_mode
);
9423 reg_nick_change_func(handle_nick_change
);
9424 reg_auth_func(handle_auth
);
9428 reg_handle_rename_func(handle_rename
);
9429 reg_unreg_func(handle_unreg
);
9431 handle_dnrs
= dict_new();
9432 dict_set_free_data(handle_dnrs
, free
);
9433 plain_dnrs
= dict_new();
9434 dict_set_free_data(plain_dnrs
, free
);
9435 mask_dnrs
= dict_new();
9436 dict_set_free_data(mask_dnrs
, free
);
9438 reg_svccmd_unbind_func(handle_svccmd_unbind
);
9439 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
9440 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
9441 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9442 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
9443 DEFINE_COMMAND(dnrsearch
, 3, 0, "template", "noregister", NULL
);
9444 modcmd_register(chanserv_module
, "dnrsearch print", NULL
, 0, 0, NULL
);
9445 modcmd_register(chanserv_module
, "dnrsearch remove", NULL
, 0, 0, NULL
);
9446 modcmd_register(chanserv_module
, "dnrsearch count", NULL
, 0, 0, NULL
);
9447 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
9448 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9449 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
9450 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
9451 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
9453 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
9455 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
9456 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
9458 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9459 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9460 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9461 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9462 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9464 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
9465 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
9466 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
9467 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9468 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9469 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9471 DEFINE_COMMAND(levels
, 1, 0, NULL
);
9473 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9474 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
9475 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9476 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
9478 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
9479 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
9480 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9481 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9482 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
9483 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
9484 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9485 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9486 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9487 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
9489 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9490 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9491 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9492 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
9493 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
9494 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
9495 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
9496 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
9497 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
9498 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
9499 DEFINE_CHANNEL_OPTION(unreviewed
);
9500 modcmd_register(chanserv_module
, "set unreviewed on", NULL
, 0, 0, "flags", "+helping", NULL
);
9501 modcmd_register(chanserv_module
, "set unreviewed off", NULL
, 0, 0, "flags", "+oper", NULL
);
9502 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
9503 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
9504 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9505 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9507 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9508 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9509 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9510 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9512 /* if you change dellamer access, see also places
9513 * like unbanme which have manager hardcoded. */
9514 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9515 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
9517 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
9519 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
9521 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9522 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9523 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9524 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9525 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9526 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9527 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9528 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9529 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9530 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9531 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9532 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9534 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
9535 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
9537 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
9538 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
9539 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
9540 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
9542 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9543 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9544 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
9545 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
9546 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
9548 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9549 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9550 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9551 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9552 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9553 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9554 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9555 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9557 /* Channel options */
9558 DEFINE_CHANNEL_OPTION(defaulttopic
);
9559 DEFINE_CHANNEL_OPTION(topicmask
);
9560 DEFINE_CHANNEL_OPTION(greeting
);
9561 DEFINE_CHANNEL_OPTION(usergreeting
);
9562 DEFINE_CHANNEL_OPTION(modes
);
9563 DEFINE_CHANNEL_OPTION(enfops
);
9564 DEFINE_CHANNEL_OPTION(enfhalfops
);
9565 DEFINE_CHANNEL_OPTION(automode
);
9566 DEFINE_CHANNEL_OPTION(protect
);
9567 DEFINE_CHANNEL_OPTION(enfmodes
);
9568 DEFINE_CHANNEL_OPTION(enftopic
);
9569 DEFINE_CHANNEL_OPTION(pubcmd
);
9570 DEFINE_CHANNEL_OPTION(userinfo
);
9571 DEFINE_CHANNEL_OPTION(dynlimit
);
9572 DEFINE_CHANNEL_OPTION(topicsnarf
);
9573 DEFINE_CHANNEL_OPTION(nodelete
);
9574 DEFINE_CHANNEL_OPTION(toys
);
9575 DEFINE_CHANNEL_OPTION(setters
);
9576 DEFINE_CHANNEL_OPTION(topicrefresh
);
9577 DEFINE_CHANNEL_OPTION(resync
);
9578 DEFINE_CHANNEL_OPTION(ctcpreaction
);
9579 DEFINE_CHANNEL_OPTION(bantimeout
);
9580 DEFINE_CHANNEL_OPTION(inviteme
);
9582 DEFINE_CHANNEL_OPTION(offchannel
);
9583 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
9585 /* Alias set topic to set defaulttopic for compatibility. */
9586 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
9589 DEFINE_USER_OPTION(autoinvite
);
9590 DEFINE_USER_OPTION(autojoin
);
9591 DEFINE_USER_OPTION(info
);
9592 DEFINE_USER_OPTION(autoop
);
9594 /* Alias uset autovoice to uset autoop. */
9595 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
9597 note_types
= dict_new();
9598 dict_set_free_data(note_types
, chanserv_deref_note_type
);
9601 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
9602 chanserv
= AddLocalUser(nick
, nick
, NULL
, "Channel Services", modes
);
9603 service_register(chanserv
)->trigger
= '!';
9604 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
9607 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
9609 if(chanserv_conf
.channel_expire_frequency
)
9610 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
9612 if(chanserv_conf
.dnr_expire_frequency
)
9613 timeq_add(now
+ chanserv_conf
.dnr_expire_frequency
, expire_dnrs
, NULL
);
9615 if(chanserv_conf
.ban_timeout_frequency
)
9616 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
9618 if(chanserv_conf
.refresh_period
)
9620 time_t next_refresh
;
9621 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
9622 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
9623 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
9626 if (autojoin_channels
&& chanserv
) {
9627 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
9628 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
9629 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
9633 reg_exit_func(chanserv_db_cleanup
);
9634 message_register_table(msgtab
);