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 2 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_BAN_TIMEOUT_FREQ "ban_timeout_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_VERSION_CONTROL "version_control"
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
65 /* version control paramiter */
66 #define KEY_VERSION_NUMBER "version_number"
68 /* Note type parameters */
69 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
70 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
71 #define KEY_NOTE_SETTER_ACCESS "setter_access"
72 #define KEY_NOTE_VISIBILITY "visibility"
73 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
74 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
75 #define KEY_NOTE_VIS_ALL "all"
76 #define KEY_NOTE_MAX_LENGTH "max_length"
77 #define KEY_NOTE_SETTER "setter"
78 #define KEY_NOTE_NOTE "note"
80 /* Do-not-register channels */
82 #define KEY_DNR_SET "set"
83 #define KEY_DNR_SETTER "setter"
84 #define KEY_DNR_REASON "reason"
87 #define KEY_REGISTERED "registered"
88 #define KEY_REGISTRAR "registrar"
89 #define KEY_SUSPENDED "suspended"
90 #define KEY_PREVIOUS "previous"
91 #define KEY_SUSPENDER "suspender"
92 #define KEY_ISSUED "issued"
93 #define KEY_REVOKED "revoked"
94 #define KEY_SUSPEND_EXPIRES "suspend_expires"
95 #define KEY_SUSPEND_REASON "suspend_reason"
96 #define KEY_GIVEOWNERSHIP "giveownership"
97 #define KEY_STAFF_ISSUER "staff_issuer"
98 #define KEY_OLD_OWNER "old_owner"
99 #define KEY_TARGET "target"
100 #define KEY_TARGET_ACCESS "target_access"
101 #define KEY_VISITED "visited"
102 #define KEY_TOPIC "topic"
103 #define KEY_GREETING "greeting"
104 #define KEY_USER_GREETING "user_greeting"
105 #define KEY_MODES "modes"
106 #define KEY_FLAGS "flags"
107 #define KEY_OPTIONS "options"
108 #define KEY_USERS "users"
109 #define KEY_BANS "bans" /* for lamers */
110 #define KEY_MAX "max"
111 #define KEY_NOTES "notes"
112 #define KEY_TOPIC_MASK "topic_mask"
113 #define KEY_OWNER_TRANSFER "owner_transfer"
116 #define KEY_LEVEL "level"
117 #define KEY_INFO "info"
118 #define KEY_SEEN "seen"
119 #define KEY_EXPIRY "expiry"
122 #define KEY_OWNER "owner"
123 #define KEY_REASON "reason"
124 #define KEY_SET "set"
125 #define KEY_DURATION "duration"
126 #define KEY_EXPIRES "expires"
127 #define KEY_TRIGGERED "triggered"
129 #define KEY_GOD_TIMEOUT "god_timeout"
131 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
132 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
134 /* Administrative messages */
135 static const struct message_entry msgtab
[] = {
136 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
138 /* Channel registration */
139 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
140 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
141 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
142 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
143 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
144 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
145 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
146 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
148 /* Do-not-register channels */
149 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
150 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
151 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
152 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
153 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
154 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
155 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
156 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
157 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
158 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
159 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
161 /* Channel unregistration */
162 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
163 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
164 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
165 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
168 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
169 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
171 /* Channel merging */
172 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
173 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
174 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
175 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
176 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
178 /* Handle unregistration */
179 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
182 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
183 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
184 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
185 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
186 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
187 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
188 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
189 { "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." },
190 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
191 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
192 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
193 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
194 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
195 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
197 /* Removing yourself from a channel. */
198 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
199 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
200 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
202 /* User management */
203 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
204 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
205 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
206 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
207 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
208 { "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." },
209 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
210 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
211 { "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." },
212 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
213 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
214 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
215 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
216 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
217 { "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" },
218 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
220 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
221 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
222 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
223 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
224 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
225 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
226 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
229 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
230 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
231 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
232 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
233 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
234 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
235 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
236 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
237 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
238 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
239 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
240 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
241 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
242 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
243 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
244 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
245 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
247 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
249 /* Channel management */
250 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
251 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
252 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
254 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
255 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
256 { "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" },
257 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
258 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
259 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
260 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
262 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
263 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
264 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
265 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
266 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
267 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
268 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
269 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
270 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
271 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
272 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
273 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
274 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
275 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
276 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
277 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
278 { "CSMSG_SET_MODES", "$bModes $b %s" },
279 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
280 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
281 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
282 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
283 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
284 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
285 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
286 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
287 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
288 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
289 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
290 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
291 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
292 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
293 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
294 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
295 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
296 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
297 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
299 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
300 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
301 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
302 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
303 { "CSMSG_USET_INFO", "$bInfo $b %s" },
305 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
306 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
307 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
308 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
309 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
310 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
311 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
312 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
313 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
314 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
315 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
317 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
318 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
319 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
320 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
321 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
322 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
323 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
325 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
326 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
327 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
328 { "CSMSG_PROTECT_NONE", "No users will be protected." },
329 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
330 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
331 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
333 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
334 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
335 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
336 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
337 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
339 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
340 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
341 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
342 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
343 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
345 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
346 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
347 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
348 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
349 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
351 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
352 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
353 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
354 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
355 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
356 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
358 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
359 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
360 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
361 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
362 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
363 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
364 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
365 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
366 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
368 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
369 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
370 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
372 /* Channel userlist */
373 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
374 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
375 /* uncomment if needed to adujust styles (and change code below)
376 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
377 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
378 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
379 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
380 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
381 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
383 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
384 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
385 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
387 /* Channel note list */
388 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
389 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
390 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
391 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
392 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
393 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
394 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
395 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
396 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
397 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
398 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
399 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
400 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
401 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
402 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
404 /* Channel [un]suspension */
405 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
406 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
407 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
408 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
409 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
410 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
411 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
413 /* Access information */
414 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
415 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
416 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
417 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
418 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
419 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
420 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
421 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
422 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
423 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
424 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
426 /* Seen information */
427 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
428 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
429 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
430 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
432 /* Names information */
433 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
434 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
436 /* Channel information */
437 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
438 { "CSMSG_BAR", "----------------------------------------"},
439 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
440 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
441 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
442 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
443 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
444 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
445 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
446 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
447 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
448 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
449 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
452 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
453 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
454 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
455 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
456 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
457 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
458 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
459 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
460 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
461 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
462 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
463 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
464 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
466 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
467 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
468 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
469 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
470 { "CSMSG_PEEK_OPS", "$bOps:$b" },
471 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
472 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
474 /* Network information */
475 { "CSMSG_NETWORK_INFO", "Network Information:" },
476 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
477 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
478 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
479 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
480 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
481 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
482 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
483 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
486 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
487 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
488 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
490 /* Channel searches */
491 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
492 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
493 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
494 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
496 /* Channel configuration */
497 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
498 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
499 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
500 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
503 { "CSMSG_USER_OPTIONS", "User Options:" },
504 // { "CSMSG_USER_PROTECTED", "That user is protected." },
507 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
508 { "CSMSG_PING_RESPONSE", "Pong!" },
509 { "CSMSG_WUT_RESPONSE", "wut" },
510 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
511 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
512 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
513 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
514 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
515 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
516 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
519 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
520 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
521 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
522 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
526 /* eject_user and unban_user flags */
527 #define ACTION_KICK 0x0001
528 #define ACTION_BAN 0x0002
529 #define ACTION_ADD_LAMER 0x0004
530 #define ACTION_ADD_TIMED_LAMER 0x0008
531 #define ACTION_UNBAN 0x0010
532 #define ACTION_DEL_LAMER 0x0020
534 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
535 #define MODELEN 40 + KEYLEN
539 #define CSFUNC_ARGS user, channel, argc, argv, cmd
541 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
542 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
543 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
544 reply("MSG_MISSING_PARAMS", argv[0]); \
548 DECLARE_LIST(dnrList
, struct do_not_register
*);
549 DEFINE_LIST(dnrList
, struct do_not_register
*);
551 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
553 struct userNode
*chanserv
;
556 extern struct string_list
*autojoin_channels
;
557 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
558 static struct log_type
*CS_LOG
;
559 struct adduserPending
* adduser_pendings
= NULL
;
560 unsigned int adduser_pendings_count
= 0;
561 unsigned long god_timeout
;
565 struct channelList support_channels
;
566 struct mod_chanmode default_modes
;
568 unsigned long db_backup_frequency
;
569 unsigned long channel_expire_frequency
;
570 unsigned long ban_timeout_frequency
;
573 unsigned int adjust_delay
;
574 long channel_expire_delay
;
575 unsigned int nodelete_level
;
577 unsigned int adjust_threshold
;
578 int join_flood_threshold
;
580 unsigned int greeting_length
;
581 unsigned int refresh_period
;
582 unsigned int giveownership_period
;
584 unsigned int max_owned
;
585 unsigned int max_chan_users
;
586 unsigned int max_chan_bans
; /* lamers */
587 unsigned int max_userinfo_length
;
589 struct string_list
*set_shows
;
590 struct string_list
*eightball
;
591 struct string_list
*old_ban_names
;
593 const char *ctcp_short_ban_duration
;
594 const char *ctcp_long_ban_duration
;
596 const char *irc_operator_epithet
;
597 const char *network_helper_epithet
;
598 const char *support_helper_epithet
;
603 struct userNode
*user
;
604 struct userNode
*bot
;
605 struct chanNode
*channel
;
607 unsigned short lowest
;
608 unsigned short highest
;
609 struct userData
**users
;
610 struct helpfile_table table
;
613 enum note_access_type
615 NOTE_SET_CHANNEL_ACCESS
,
616 NOTE_SET_CHANNEL_SETTER
,
620 enum note_visible_type
623 NOTE_VIS_CHANNEL_USERS
,
629 enum note_access_type set_access_type
;
631 unsigned int min_opserv
;
632 unsigned short min_ulevel
;
634 enum note_visible_type visible_type
;
635 unsigned int max_length
;
642 struct note_type
*type
;
643 char setter
[NICKSERV_HANDLE_LEN
+1];
647 static unsigned int registered_channels
;
648 static unsigned int banCount
;
650 static const struct {
653 unsigned short level
;
655 } accessLevels
[] = { /* MUST be orderd less to most! */
656 { "peon", "Peon", UL_PEON
, '+' },
657 { "halfop", "HalfOp", UL_HALFOP
, '%' },
658 { "op", "Op", UL_OP
, '@' },
659 { "manager", "Manager", UL_MANAGER
, '%' },
660 { "coowner", "Coowner", UL_COOWNER
, '*' },
661 { "owner", "Owner", UL_OWNER
, '!' },
662 { "helper", "BUG:", UL_HELPER
, 'X' }
665 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
666 static const struct {
669 unsigned short default_value
;
670 unsigned int old_idx
;
671 unsigned int old_flag
;
672 unsigned short flag_value
;
674 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
675 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
676 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
677 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
678 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
679 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
680 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
681 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
682 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
685 struct charOptionValues
{
688 } automodeValues
[] = {
689 { 'n', "CSMSG_AUTOMODE_NONE" },
690 { 'y', "CSMSG_AUTOMODE_NORMAL" },
691 { 'v', "CSMSG_AUTOMODE_VOICE" },
692 { 'h', "CSMSG_AUTOMODE_HOP" },
693 { 'o', "CSMSG_AUTOMODE_OP" },
694 { 'm', "CSMSG_AUTOMODE_MUTE" },
695 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
696 }, protectValues
[] = {
697 { 'a', "CSMSG_PROTECT_ALL" },
698 { 'e', "CSMSG_PROTECT_EQUAL" },
699 { 'l', "CSMSG_PROTECT_LOWER" },
700 { 'n', "CSMSG_PROTECT_NONE" }
702 { 'd', "CSMSG_TOYS_DISABLED" },
703 { 'n', "CSMSG_TOYS_PRIVATE" },
704 { 'p', "CSMSG_TOYS_PUBLIC" }
705 }, topicRefreshValues
[] = {
706 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
707 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
708 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
709 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
710 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
711 }, ctcpReactionValues
[] = {
712 { 'n', "CSMSG_CTCPREACTION_NONE" },
713 { 'k', "CSMSG_CTCPREACTION_KICK" },
714 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
715 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
716 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
717 }, banTimeoutValues
[] = {
718 { '0', "CSMSG_BANTIMEOUT_NONE" },
719 { '1', "CSMSG_BANTIMEOUT_10M" },
720 { '2', "CSMSG_BANTIMEOUT_2H" },
721 { '3', "CSMSG_BANTIMEOUT_4H" },
722 { '4', "CSMSG_BANTIMEOUT_1D" },
723 { '5', "CSMSG_BANTIMEOUT_1W" }
726 { 'n', "CSMSG_RESYNC_NEVER" },
727 { '1', "CSMSG_RESYNC_3_HOURS" },
728 { '2', "CSMSG_RESYNC_6_HOURS" },
729 { '3', "CSMSG_RESYNC_12_HOURS" },
730 { '4', "CSMSG_RESYNC_24_HOURS" }
733 static const struct {
737 unsigned int old_idx
;
739 struct charOptionValues
*values
;
741 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
742 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
743 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
744 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
745 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
746 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
747 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
750 struct userData
*helperList
;
751 struct chanData
*channelList
;
752 static struct module *chanserv_module
;
753 static unsigned int userCount
;
754 unsigned int chanserv_read_version
= 0; /* db version control */
756 #define CHANSERV_DB_VERSION 2
758 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
759 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
762 user_level_from_name(const char *name
, unsigned short clamp_level
)
764 unsigned int level
= 0, ii
;
766 level
= strtoul(name
, NULL
, 10);
767 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
768 if(!irccasecmp(name
, accessLevels
[ii
].name
))
769 level
= accessLevels
[ii
].level
;
770 if(level
> clamp_level
)
776 user_level_name_from_level(int level
)
784 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
785 if(level
>= accessLevels
[ii
].level
)
786 highest
= accessLevels
[ii
].title
;
792 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
795 *minl
= strtoul(arg
, &sep
, 10);
803 *maxl
= strtoul(sep
+1, &sep
, 10);
811 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
813 struct userData
*uData
, **head
;
815 if(!channel
|| !handle
)
818 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
819 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
821 for(uData
= helperList
;
822 uData
&& uData
->handle
!= handle
;
823 uData
= uData
->next
);
827 uData
= calloc(1, sizeof(struct userData
));
828 uData
->handle
= handle
;
830 uData
->access
= UL_HELPER
;
836 uData
->next
= helperList
;
838 helperList
->prev
= uData
;
846 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
847 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
850 head
= &(channel
->users
);
853 if(uData
&& (uData
!= *head
))
855 /* Shuffle the user to the head of whatever list he was in. */
857 uData
->next
->prev
= uData
->prev
;
859 uData
->prev
->next
= uData
->next
;
865 (**head
).prev
= uData
;
872 /* Returns non-zero if user has at least the minimum access.
873 * exempt_owner is set when handling !set, so the owner can set things
876 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
878 struct userData
*uData
;
879 struct chanData
*cData
= channel
->channel_info
;
880 unsigned short minimum
= cData
->lvlOpts
[opt
];
883 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
886 if(minimum
<= uData
->access
)
888 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
893 /* Scan for other users authenticated to the same handle
894 still in the channel. If so, keep them listed as present.
896 user is optional, if not null, it skips checking that userNode
897 (for the handle_part function) */
899 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
903 if(IsSuspended(uData
->channel
)
904 || IsUserSuspended(uData
)
905 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
917 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
919 unsigned int eflags
, argc
;
921 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
923 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
924 if(!channel
->channel_info
925 || IsSuspended(channel
->channel_info
)
927 || !ircncasecmp(text
, "ACTION ", 7))
929 /* We dont punish people we know -Rubin
930 * * Figure out the minimum level needed to CTCP the channel *
932 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
935 /* If they are a user of the channel, they are exempt */
936 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
938 /* We need to enforce against them; do so. */
941 argv
[1] = user
->nick
;
943 if(GetUserMode(channel
, user
))
944 eflags
|= ACTION_KICK
;
945 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
946 default: case 'n': return;
948 eflags
|= ACTION_KICK
;
951 eflags
|= ACTION_BAN
;
954 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
955 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
958 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
959 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
962 argv
[argc
++] = bad_ctcp_reason
;
963 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
967 chanserv_create_note_type(const char *name
)
969 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
970 strcpy(ntype
->name
, name
);
972 dict_insert(note_types
, ntype
->name
, ntype
);
977 chanserv_deref_note_type(void *data
)
979 struct note_type
*ntype
= data
;
981 if(--ntype
->refs
> 0)
987 chanserv_flush_note_type(struct note_type
*ntype
)
989 struct chanData
*cData
;
990 for(cData
= channelList
; cData
; cData
= cData
->next
)
991 dict_remove(cData
->notes
, ntype
->name
);
995 chanserv_truncate_notes(struct note_type
*ntype
)
997 struct chanData
*cData
;
999 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1001 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1002 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1005 if(strlen(note
->note
) <= ntype
->max_length
)
1007 dict_remove2(cData
->notes
, ntype
->name
, 1);
1008 note
= realloc(note
, size
);
1009 note
->note
[ntype
->max_length
] = 0;
1010 dict_insert(cData
->notes
, ntype
->name
, note
);
1014 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1016 static struct note
*
1017 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1020 unsigned int len
= strlen(text
);
1022 if(len
> type
->max_length
) len
= type
->max_length
;
1023 note
= calloc(1, sizeof(*note
) + len
);
1025 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1026 memcpy(note
->note
, text
, len
);
1027 note
->note
[len
] = 0;
1028 dict_insert(channel
->notes
, type
->name
, note
);
1034 chanserv_free_note(void *data
)
1036 struct note
*note
= data
;
1038 chanserv_deref_note_type(note
->type
);
1039 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1043 static MODCMD_FUNC(cmd_createnote
) {
1044 struct note_type
*ntype
;
1045 unsigned int arg
= 1, existed
= 0, max_length
;
1047 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1050 ntype
= chanserv_create_note_type(argv
[arg
]);
1051 if(!irccasecmp(argv
[++arg
], "privileged"))
1054 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1055 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1057 else if(!irccasecmp(argv
[arg
], "channel"))
1059 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1062 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1065 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1066 ntype
->set_access
.min_ulevel
= ulvl
;
1068 else if(!irccasecmp(argv
[arg
], "setter"))
1070 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1074 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1078 if(!irccasecmp(argv
[++arg
], "privileged"))
1079 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1080 else if(!irccasecmp(argv
[arg
], "channel_users"))
1081 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1082 else if(!irccasecmp(argv
[arg
], "all"))
1083 ntype
->visible_type
= NOTE_VIS_ALL
;
1085 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1089 if((arg
+1) >= argc
) {
1090 reply("MSG_MISSING_PARAMS", argv
[0]);
1093 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1094 if(max_length
< 20 || max_length
> 450)
1096 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1099 if(existed
&& (max_length
< ntype
->max_length
))
1101 ntype
->max_length
= max_length
;
1102 chanserv_truncate_notes(ntype
);
1104 ntype
->max_length
= max_length
;
1107 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1109 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1114 dict_remove(note_types
, ntype
->name
);
1118 static MODCMD_FUNC(cmd_removenote
) {
1119 struct note_type
*ntype
;
1122 ntype
= dict_find(note_types
, argv
[1], NULL
);
1123 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1126 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1133 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1136 chanserv_flush_note_type(ntype
);
1138 dict_remove(note_types
, argv
[1]);
1139 reply("CSMSG_NOTE_DELETED", argv
[1]);
1144 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1148 if(orig
->modes_set
& change
->modes_clear
)
1150 if(orig
->modes_clear
& change
->modes_set
)
1152 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1153 && strcmp(orig
->new_key
, change
->new_key
))
1155 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1156 && (orig
->new_limit
!= change
->new_limit
))
1161 static char max_length_text
[MAXLEN
+1][16];
1163 static struct helpfile_expansion
1164 chanserv_expand_variable(const char *variable
)
1166 struct helpfile_expansion exp
;
1168 if(!irccasecmp(variable
, "notes"))
1171 exp
.type
= HF_TABLE
;
1172 exp
.value
.table
.length
= 1;
1173 exp
.value
.table
.width
= 3;
1174 exp
.value
.table
.flags
= 0;
1175 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1176 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1177 exp
.value
.table
.contents
[0][0] = "Note Type";
1178 exp
.value
.table
.contents
[0][1] = "Visibility";
1179 exp
.value
.table
.contents
[0][2] = "Max Length";
1180 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1182 struct note_type
*ntype
= iter_data(it
);
1185 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1186 row
= exp
.value
.table
.length
++;
1187 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1188 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1189 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1190 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1192 if(!max_length_text
[ntype
->max_length
][0])
1193 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1194 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1199 exp
.type
= HF_STRING
;
1200 exp
.value
.str
= NULL
;
1204 static struct chanData
*
1205 register_channel(struct chanNode
*cNode
, char *registrar
)
1207 struct chanData
*channel
;
1208 enum levelOption lvlOpt
;
1209 enum charOption chOpt
;
1211 channel
= calloc(1, sizeof(struct chanData
));
1213 channel
->notes
= dict_new();
1214 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1216 channel
->registrar
= strdup(registrar
);
1217 channel
->registered
= now
;
1218 channel
->visited
= now
;
1219 channel
->limitAdjusted
= now
;
1220 channel
->ownerTransfer
= now
;
1221 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1222 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1223 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1224 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1225 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1227 channel
->prev
= NULL
;
1228 channel
->next
= channelList
;
1231 channelList
->prev
= channel
;
1232 channelList
= channel
;
1233 registered_channels
++;
1235 channel
->channel
= cNode
;
1237 cNode
->channel_info
= channel
;
1242 static struct userData
*
1243 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
, time_t expiry
)
1245 struct userData
*ud
;
1247 if(access
> UL_OWNER
)
1250 ud
= calloc(1, sizeof(*ud
));
1251 ud
->channel
= channel
;
1252 ud
->handle
= handle
;
1254 ud
->access
= access
;
1255 ud
->info
= info
? strdup(info
) : NULL
;
1256 ud
->expiry
= expiry
;
1259 ud
->next
= channel
->users
;
1261 channel
->users
->prev
= ud
;
1262 channel
->users
= ud
;
1264 channel
->userCount
++;
1268 ud
->u_next
= ud
->handle
->channels
;
1270 ud
->u_next
->u_prev
= ud
;
1271 ud
->handle
->channels
= ud
;
1273 ud
->flags
= USER_FLAGS_DEFAULT
;
1277 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1280 chanserv_expire_tempuser(void *data
)
1282 struct userData
*uData
= data
;
1286 handle
= strdup(uData
->handle
->handle
);
1287 if (uData
->expiry
> 0) {
1288 if (uData
->present
) {
1289 struct userNode
*user
, *next_un
= NULL
;
1290 struct handle_info
*hi
;
1292 hi
= get_handle_info(handle
);
1293 for (user
= hi
->users
; user
; user
= next_un
) {
1294 struct mod_chanmode
*change
;
1295 struct modeNode
*mn
;
1296 unsigned int count
= 0;
1298 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1299 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1300 next_un
= user
->next_authed
;
1304 change
= mod_chanmode_alloc(2);
1305 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1306 change
->args
[count
++].u
.member
= mn
;
1309 change
->argc
= count
;
1310 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1312 mod_chanmode_free(change
);
1313 next_un
= user
->next_authed
;
1316 del_channel_user(uData
, 1);
1322 del_channel_user(struct userData
*user
, int do_gc
)
1324 struct chanData
*channel
= user
->channel
;
1326 channel
->userCount
--;
1329 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1332 user
->prev
->next
= user
->next
;
1334 channel
->users
= user
->next
;
1336 user
->next
->prev
= user
->prev
;
1339 user
->u_prev
->u_next
= user
->u_next
;
1341 user
->handle
->channels
= user
->u_next
;
1343 user
->u_next
->u_prev
= user
->u_prev
;
1347 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1348 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1349 unregister_channel(channel
, "lost all users.");
1353 static struct adduserPending
*
1354 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1356 struct adduserPending
*ap
;
1357 ap
= calloc(1,sizeof(struct adduserPending
));
1358 ap
->channel
= channel
;
1361 ap
->created
= time(NULL
);
1363 /* ap->prev defaults to NULL already.. */
1364 ap
->next
= adduser_pendings
;
1365 if(adduser_pendings
)
1366 adduser_pendings
->prev
= ap
;
1367 adduser_pendings
= ap
;
1368 adduser_pendings_count
++;
1373 del_adduser_pending(struct adduserPending
*ap
)
1376 ap
->prev
->next
= ap
->next
;
1378 adduser_pendings
= ap
->next
;
1381 ap
->next
->prev
= ap
->prev
;
1385 static void expire_adduser_pending();
1387 /* find_adduser_pending(channel, user) will find an arbitrary record
1388 * from user, channel, or user and channel.
1389 * if user or channel are NULL, they will match any records.
1391 static struct adduserPending
*
1392 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1394 struct adduserPending
*ap
;
1396 expire_adduser_pending(); /* why not here.. */
1398 if(!channel
&& !user
) /* 2 nulls matches all */
1399 return(adduser_pendings
);
1400 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1402 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1409 /* Remove all pendings for a user or channel
1411 * called in nickserv.c DelUser() and proto-* unregister_channel()
1414 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1416 struct adduserPending
*ap
;
1418 /* So this is a bit wastefull, i hate dealing with linked lists.
1419 * if its a problem we'll rewrite it right */
1420 while((ap
= find_adduser_pending(channel
, user
))) {
1421 del_adduser_pending(ap
);
1425 /* Called from nickserv.c cmd_auth after someone auths */
1427 process_adduser_pending(struct userNode
*user
)
1429 struct adduserPending
*ap
;
1430 if(!user
->handle_info
)
1431 return; /* not associated with an account */
1432 while((ap
= find_adduser_pending(NULL
, user
)))
1434 struct userData
*actee
;
1435 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1437 /* Already on the userlist. do nothing*/
1441 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1442 scan_user_presence(actee
, NULL
);
1444 del_adduser_pending(ap
);
1449 expire_adduser_pending()
1451 struct adduserPending
*ap
, *ap_next
;
1452 ap
= adduser_pendings
;
1455 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1457 ap_next
= ap
->next
; /* save next */
1458 del_adduser_pending(ap
); /* free and relink */
1459 ap
= ap_next
; /* advance */
1466 static void expire_ban(void *data
);
1469 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1472 unsigned int ii
, l1
, l2
;
1477 bd
= malloc(sizeof(struct banData
));
1479 bd
->channel
= channel
;
1481 bd
->triggered
= triggered
;
1482 bd
->expires
= expires
;
1484 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1486 extern const char *hidden_host_suffix
;
1487 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1491 l2
= strlen(old_name
);
1494 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1496 new_mask
= alloca(MAXLEN
);
1497 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1500 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1502 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1503 bd
->reason
= strdup(reason
);
1506 timeq_add(expires
, expire_ban
, bd
);
1509 bd
->next
= channel
->bans
; /* lamers */
1511 channel
->bans
->prev
= bd
;
1513 channel
->banCount
++;
1520 del_channel_ban(struct banData
*ban
)
1522 ban
->channel
->banCount
--;
1526 ban
->prev
->next
= ban
->next
;
1528 ban
->channel
->bans
= ban
->next
;
1531 ban
->next
->prev
= ban
->prev
;
1534 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1543 expire_ban(void *data
) /* lamer.. */
1545 struct banData
*bd
= data
;
1546 if(!IsSuspended(bd
->channel
))
1548 struct banList bans
;
1549 struct mod_chanmode change
;
1551 bans
= bd
->channel
->channel
->banlist
;
1552 mod_chanmode_init(&change
);
1553 for(ii
=0; ii
<bans
.used
; ii
++)
1555 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1558 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1559 change
.args
[0].u
.hostmask
= bd
->mask
;
1560 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1566 del_channel_ban(bd
);
1569 static void chanserv_expire_suspension(void *data
);
1572 unregister_channel(struct chanData
*channel
, const char *reason
)
1574 struct mod_chanmode change
;
1575 char msgbuf
[MAXLEN
];
1577 /* After channel unregistration, the following must be cleaned
1579 - Channel information.
1581 - Channel bans. (lamers)
1582 - Channel suspension data.
1583 - adduser_pending data.
1584 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1590 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1594 mod_chanmode_init(&change
);
1595 change
.modes_clear
|= MODE_REGISTERED
;
1596 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1599 wipe_adduser_pending(channel
->channel
, NULL
);
1601 while(channel
->users
)
1602 del_channel_user(channel
->users
, 0);
1604 while(channel
->bans
)
1605 del_channel_ban(channel
->bans
);
1607 free(channel
->topic
);
1608 free(channel
->registrar
);
1609 free(channel
->greeting
);
1610 free(channel
->user_greeting
);
1611 free(channel
->topic_mask
);
1614 channel
->prev
->next
= channel
->next
;
1616 channelList
= channel
->next
;
1619 channel
->next
->prev
= channel
->prev
;
1621 if(channel
->suspended
)
1623 struct chanNode
*cNode
= channel
->channel
;
1624 struct suspended
*suspended
, *next_suspended
;
1626 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1628 next_suspended
= suspended
->previous
;
1629 free(suspended
->suspender
);
1630 free(suspended
->reason
);
1631 if(suspended
->expires
)
1632 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1637 cNode
->channel_info
= NULL
;
1639 channel
->channel
->channel_info
= NULL
;
1641 dict_delete(channel
->notes
);
1642 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1643 if(!IsSuspended(channel
))
1644 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1645 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1646 UnlockChannel(channel
->channel
);
1648 registered_channels
--;
1652 expire_channels(UNUSED_ARG(void *data
))
1654 struct chanData
*channel
, *next
;
1655 struct userData
*user
;
1656 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1658 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1659 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1661 for(channel
= channelList
; channel
; channel
= next
)
1663 next
= channel
->next
;
1665 /* See if the channel can be expired. */
1666 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1667 || IsProtected(channel
))
1670 /* Make sure there are no high-ranking users still in the channel. */
1671 for(user
=channel
->users
; user
; user
=user
->next
)
1672 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1677 /* Unregister the channel */
1678 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1679 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1680 unregister_channel(channel
, "registration expired.");
1683 if(chanserv_conf
.channel_expire_frequency
)
1684 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1688 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1690 char protect
= channel
->chOpts
[chProtect
];
1691 struct userData
*cs_victim
, *cs_aggressor
;
1693 /* If victim access level is greater than set invitelevel, don't let
1694 * us kick them, but don't consider it punishment if someone else does
1698 if(victim
== aggressor
)
1700 /* Don't protect if the victim isn't authenticated (because they
1701 can't be a channel user), unless we are to protect non-users
1704 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1706 /* If they have enough access to invite themselvs through a ban,
1707 * and its us kicking them, don't. -Rubin */
1708 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1714 if(protect
!= 'a' && !cs_victim
)
1717 /* Protect if the aggressor isn't a user because at this point,
1718 the aggressor can only be less than or equal to the victim. */
1720 /* Not protected from chanserv except above */
1721 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1722 if(aggressor
== chanserv
)
1725 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1729 /* If the aggressor was a user, then the victim can't be helped. */
1736 if(cs_victim
->access
> cs_aggressor
->access
)
1741 if(cs_victim
->access
>= cs_aggressor
->access
)
1750 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1752 struct chanData
*cData
= channel
->channel_info
;
1753 struct userData
*cs_victim
;
1755 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1756 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1757 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1760 reply("CSMSG_OPBY_LOCKED");
1762 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1770 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1772 struct chanData
*cData
= channel
->channel_info
;
1773 struct userData
*cs_victim
;
1775 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1776 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1777 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1779 reply("CSMSG_HOPBY_LOCKED");
1788 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1790 if(IsService(victim
))
1792 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1796 if(protect_user(victim
, user
, channel
->channel_info
, false))
1798 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1806 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1808 if(IsService(victim
))
1810 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1814 if(protect_user(victim
, user
, channel
->channel_info
, false))
1816 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1823 static struct do_not_register
*
1824 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1826 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1827 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1828 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1829 strcpy(dnr
->reason
, reason
);
1831 if(dnr
->chan_name
[0] == '*')
1832 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1833 else if(strpbrk(dnr
->chan_name
, "*?"))
1834 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1836 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1840 static struct dnrList
1841 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1843 struct dnrList list
;
1845 struct do_not_register
*dnr
;
1847 dnrList_init(&list
);
1848 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1849 dnrList_append(&list
, dnr
);
1850 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1851 dnrList_append(&list
, dnr
);
1853 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1854 if(match_ircglob(chan_name
, iter_key(it
)))
1855 dnrList_append(&list
, iter_data(it
));
1860 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1862 struct dnrList list
;
1863 struct do_not_register
*dnr
;
1865 char buf
[INTERVALLEN
];
1867 list
= chanserv_find_dnrs(chan_name
, handle
);
1868 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1870 dnr
= list
.list
[ii
];
1873 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1874 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1877 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1880 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1885 struct do_not_register
*
1886 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1888 struct do_not_register
*dnr
;
1891 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1895 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1897 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1898 if(match_ircglob(chan_name
, iter_key(it
)))
1899 return iter_data(it
);
1904 static CHANSERV_FUNC(cmd_noregister
)
1907 struct do_not_register
*dnr
;
1908 char buf
[INTERVALLEN
];
1909 unsigned int matches
;
1915 reply("CSMSG_DNR_SEARCH_RESULTS");
1916 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1919 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1921 dnr
= iter_data(it
);
1923 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1925 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1928 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1930 dnr
= iter_data(it
);
1932 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1934 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1937 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1939 dnr
= iter_data(it
);
1941 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1943 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1948 reply("MSG_MATCH_COUNT", matches
);
1950 reply("MSG_NO_MATCHES");
1956 if(!IsChannelName(target
) && (*target
!= '*'))
1958 reply("CSMSG_NOT_DNR", target
);
1964 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1965 if((*target
== '*') && !get_handle_info(target
+ 1))
1967 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1970 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1971 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1975 reply("CSMSG_DNR_SEARCH_RESULTS");
1976 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1979 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1981 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1983 reply("MSG_NO_MATCHES");
1987 static CHANSERV_FUNC(cmd_allowregister
)
1989 const char *chan_name
= argv
[1];
1991 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1993 dict_remove(handle_dnrs
, chan_name
+1);
1994 reply("CSMSG_DNR_REMOVED", chan_name
);
1996 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1998 dict_remove(plain_dnrs
, chan_name
);
1999 reply("CSMSG_DNR_REMOVED", chan_name
);
2001 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
2003 dict_remove(mask_dnrs
, chan_name
);
2004 reply("CSMSG_DNR_REMOVED", chan_name
);
2008 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2015 chanserv_get_owned_count(struct handle_info
*hi
)
2017 struct userData
*cList
;
2020 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2021 if(cList
->access
== UL_OWNER
)
2026 static CHANSERV_FUNC(cmd_register
)
2028 struct handle_info
*handle
;
2029 struct chanData
*cData
;
2030 struct modeNode
*mn
;
2031 char reason
[MAXLEN
];
2033 unsigned int new_channel
, force
=0;
2034 struct do_not_register
*dnr
;
2037 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2038 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2044 if(channel
->channel_info
)
2046 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2050 if(channel
->bad_channel
)
2052 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2056 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2058 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2063 chan_name
= channel
->name
;
2069 reply("MSG_MISSING_PARAMS", cmd
->name
);
2070 svccmd_send_help_brief(user
, chanserv
, cmd
);
2073 if(!IsChannelName(argv
[1]))
2075 reply("MSG_NOT_CHANNEL_NAME");
2079 if(opserv_bad_channel(argv
[1]))
2081 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2086 chan_name
= argv
[1];
2089 if(argc
>= (new_channel
+2))
2091 if(!IsHelping(user
))
2093 reply("CSMSG_PROXY_FORBIDDEN");
2097 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2099 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2100 dnr
= chanserv_is_dnr(chan_name
, handle
);
2102 /* Check if they are over the limit.. */
2103 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2105 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2112 handle
= user
->handle_info
;
2113 dnr
= chanserv_is_dnr(chan_name
, handle
);
2114 /* Check if they are over the limit.. */
2115 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2117 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2120 /* Check if another service is in the channel */
2122 for(n
= 0; n
< channel
->members
.used
; n
++)
2124 mn
= channel
->members
.list
[n
];
2125 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2127 reply("CSMSG_ANOTHER_SERVICE");
2134 if(!IsHelping(user
))
2135 reply("CSMSG_DNR_CHANNEL", chan_name
);
2137 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2141 /* now handled above for message specilization *
2142 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2144 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2150 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2152 cData
= register_channel(channel
, user
->handle_info
->handle
);
2153 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2154 cData
->modes
= chanserv_conf
.default_modes
;
2156 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2157 if (IsOffChannel(cData
))
2159 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2163 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2164 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2165 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2167 mod_chanmode_announce(chanserv
, channel
, change
);
2168 mod_chanmode_free(change
);
2171 /* Initialize the channel's max user record. */
2172 cData
->max
= channel
->members
.used
;
2174 if(handle
!= user
->handle_info
)
2175 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2178 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2179 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2180 handle
->handle
, user
->handle_info
->handle
);
2185 make_confirmation_string(struct userData
*uData
)
2187 static char strbuf
[16];
2192 for(src
= uData
->handle
->handle
; *src
; )
2193 accum
= accum
* 31 + toupper(*src
++);
2195 for(src
= uData
->channel
->channel
->name
; *src
; )
2196 accum
= accum
* 31 + toupper(*src
++);
2197 sprintf(strbuf
, "%08x", accum
);
2201 static CHANSERV_FUNC(cmd_unregister
)
2204 char reason
[MAXLEN
];
2205 struct chanData
*cData
;
2206 struct userData
*uData
;
2208 cData
= channel
->channel_info
;
2211 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2215 uData
= GetChannelUser(cData
, user
->handle_info
);
2216 if(!uData
|| (uData
->access
< UL_OWNER
))
2218 reply("CSMSG_NO_ACCESS");
2222 if(IsProtected(cData
))
2224 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2228 if(!IsHelping(user
))
2230 const char *confirm_string
;
2231 if(IsSuspended(cData
))
2233 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2236 confirm_string
= make_confirmation_string(uData
);
2237 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2239 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2244 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2245 name
= strdup(channel
->name
);
2246 unregister_channel(cData
, reason
);
2247 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2248 reply("CSMSG_UNREG_SUCCESS", name
);
2254 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2256 extern struct userNode
*spamserv
;
2257 struct mod_chanmode
*change
;
2259 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2261 change
= mod_chanmode_alloc(2);
2263 change
->args
[0].mode
= MODE_CHANOP
;
2264 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2265 change
->args
[1].mode
= MODE_CHANOP
;
2266 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2270 change
= mod_chanmode_alloc(1);
2272 change
->args
[0].mode
= MODE_CHANOP
;
2273 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2276 mod_chanmode_announce(chanserv
, channel
, change
);
2277 mod_chanmode_free(change
);
2280 static CHANSERV_FUNC(cmd_move
)
2282 struct mod_chanmode change
;
2283 struct chanNode
*target
;
2284 struct modeNode
*mn
;
2285 struct userData
*uData
;
2286 struct do_not_register
*dnr
;
2287 int chanserv_join
= 0, spamserv_join
;
2291 if(IsProtected(channel
->channel_info
))
2293 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2297 if(!IsChannelName(argv
[1]))
2299 reply("MSG_NOT_CHANNEL_NAME");
2303 if(opserv_bad_channel(argv
[1]))
2305 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2309 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2311 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2313 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2315 if(!IsHelping(user
))
2316 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2318 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2324 mod_chanmode_init(&change
);
2325 if(!(target
= GetChannel(argv
[1])))
2327 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2328 if(!IsSuspended(channel
->channel_info
))
2331 else if(target
->channel_info
)
2333 reply("CSMSG_ALREADY_REGGED", target
->name
);
2336 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2337 && !IsHelping(user
))
2339 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2342 else if(!IsSuspended(channel
->channel_info
))
2347 /* Clear MODE_REGISTERED from old channel, add it to new. */
2349 change
.modes_clear
= MODE_REGISTERED
;
2350 mod_chanmode_announce(chanserv
, channel
, &change
);
2351 change
.modes_clear
= 0;
2352 change
.modes_set
= MODE_REGISTERED
;
2353 mod_chanmode_announce(chanserv
, target
, &change
);
2356 /* Move the channel_info to the target channel; it
2357 shouldn't be necessary to clear timeq callbacks
2358 for the old channel. */
2359 target
->channel_info
= channel
->channel_info
;
2360 target
->channel_info
->channel
= target
;
2361 channel
->channel_info
= NULL
;
2363 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2366 ss_cs_join_channel(target
, spamserv_join
);
2368 if(!IsSuspended(target
->channel_info
))
2370 char reason2
[MAXLEN
];
2371 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2372 DelChannelUser(chanserv
, channel
, reason2
, 0);
2375 UnlockChannel(channel
);
2376 LockChannel(target
);
2377 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2378 channel
->name
, target
->name
, user
->handle_info
->handle
);
2380 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2385 merge_users(struct chanData
*source
, struct chanData
*target
)
2387 struct userData
*suData
, *tuData
, *next
;
2393 /* Insert the source's users into the scratch area. */
2394 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2395 dict_insert(merge
, suData
->handle
->handle
, suData
);
2397 /* Iterate through the target's users, looking for
2398 users common to both channels. The lower access is
2399 removed from either the scratch area or target user
2401 for(tuData
= target
->users
; tuData
; tuData
= next
)
2403 struct userData
*choice
;
2405 next
= tuData
->next
;
2407 /* If a source user exists with the same handle as a target
2408 channel's user, resolve the conflict by removing one. */
2409 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2413 /* Pick the data we want to keep. */
2414 /* If the access is the same, use the later seen time. */
2415 if(suData
->access
== tuData
->access
)
2416 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2417 else /* Otherwise, keep the higher access level. */
2418 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2420 /* Remove the user that wasn't picked. */
2421 if(choice
== tuData
)
2423 dict_remove(merge
, suData
->handle
->handle
);
2424 del_channel_user(suData
, 0);
2427 del_channel_user(tuData
, 0);
2430 /* Move the remaining users to the target channel. */
2431 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2433 suData
= iter_data(it
);
2435 /* Insert the user into the target channel's linked list. */
2436 suData
->prev
= NULL
;
2437 suData
->next
= target
->users
;
2438 suData
->channel
= target
;
2441 target
->users
->prev
= suData
;
2442 target
->users
= suData
;
2444 /* Update the user counts for the target channel; the
2445 source counts are left alone. */
2446 target
->userCount
++;
2449 /* Possible to assert (source->users == NULL) here. */
2450 source
->users
= NULL
;
2455 merge_bans(struct chanData
*source
, struct chanData
*target
)
2457 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2459 /* Hold on to the original head of the target ban list
2460 to avoid comparing source bans with source bans. */
2461 tFront
= target
->bans
;
2463 /* Perform a totally expensive O(n*m) merge, ick. */
2464 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2466 /* Flag to track whether the ban's been moved
2467 to the destination yet. */
2470 /* Possible to assert (sbData->prev == NULL) here. */
2471 sNext
= sbData
->next
;
2473 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2475 tNext
= tbData
->next
;
2477 /* Perform two comparisons between each source
2478 and target ban, conflicts are resolved by
2479 keeping the broader ban and copying the later
2480 expiration and triggered time. */
2481 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2483 /* There is a broader ban in the target channel that
2484 overrides one in the source channel; remove the
2485 source ban and break. */
2486 if(sbData
->expires
> tbData
->expires
)
2487 tbData
->expires
= sbData
->expires
;
2488 if(sbData
->triggered
> tbData
->triggered
)
2489 tbData
->triggered
= sbData
->triggered
;
2490 del_channel_ban(sbData
);
2493 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2495 /* There is a broader ban in the source channel that
2496 overrides one in the target channel; remove the
2497 target ban, fall through and move the source over. */
2498 if(tbData
->expires
> sbData
->expires
)
2499 sbData
->expires
= tbData
->expires
;
2500 if(tbData
->triggered
> sbData
->triggered
)
2501 sbData
->triggered
= tbData
->triggered
;
2502 if(tbData
== tFront
)
2504 del_channel_ban(tbData
);
2507 /* Source bans can override multiple target bans, so
2508 we allow a source to run through this loop multiple
2509 times, but we can only move it once. */
2514 /* Remove the source ban from the source ban list. */
2516 sbData
->next
->prev
= sbData
->prev
;
2518 /* Modify the source ban's associated channel. */
2519 sbData
->channel
= target
;
2521 /* Insert the ban into the target channel's linked list. */
2522 sbData
->prev
= NULL
;
2523 sbData
->next
= target
->bans
;
2526 target
->bans
->prev
= sbData
;
2527 target
->bans
= sbData
;
2529 /* Update the user counts for the target channel. */
2534 /* Possible to assert (source->bans == NULL) here. */
2535 source
->bans
= NULL
;
2539 merge_data(struct chanData
*source
, struct chanData
*target
)
2541 /* Use more recent visited and owner-transfer time; use older
2542 * registered time. Bitwise or may_opchan. Use higher max.
2543 * Do not touch last_refresh, ban count or user counts.
2545 if(source
->visited
> target
->visited
)
2546 target
->visited
= source
->visited
;
2547 if(source
->registered
< target
->registered
)
2548 target
->registered
= source
->registered
;
2549 if(source
->ownerTransfer
> target
->ownerTransfer
)
2550 target
->ownerTransfer
= source
->ownerTransfer
;
2551 if(source
->may_opchan
)
2552 target
->may_opchan
= 1;
2553 if(source
->max
> target
->max
)
2554 target
->max
= source
->max
;
2558 merge_channel(struct chanData
*source
, struct chanData
*target
)
2560 merge_users(source
, target
);
2561 merge_bans(source
, target
);
2562 merge_data(source
, target
);
2565 static CHANSERV_FUNC(cmd_merge
)
2567 struct userData
*target_user
;
2568 struct chanNode
*target
;
2569 char reason
[MAXLEN
];
2573 /* Make sure the target channel exists and is registered to the user
2574 performing the command. */
2575 if(!(target
= GetChannel(argv
[1])))
2577 reply("MSG_INVALID_CHANNEL");
2581 if(!target
->channel_info
)
2583 reply("CSMSG_NOT_REGISTERED", target
->name
);
2587 if(IsProtected(channel
->channel_info
))
2589 reply("CSMSG_MERGE_NODELETE");
2593 if(IsSuspended(target
->channel_info
))
2595 reply("CSMSG_MERGE_SUSPENDED");
2599 if(channel
== target
)
2601 reply("CSMSG_MERGE_SELF");
2605 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2606 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2608 reply("CSMSG_MERGE_NOT_OWNER");
2612 /* Merge the channel structures and associated data. */
2613 merge_channel(channel
->channel_info
, target
->channel_info
);
2614 spamserv_cs_move_merge(user
, channel
, target
, 0);
2615 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2616 unregister_channel(channel
->channel_info
, reason
);
2617 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2621 static CHANSERV_FUNC(cmd_opchan
)
2623 struct mod_chanmode change
;
2624 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2626 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2629 channel
->channel_info
->may_opchan
= 0;
2630 mod_chanmode_init(&change
);
2632 change
.args
[0].mode
= MODE_CHANOP
;
2633 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2634 mod_chanmode_announce(chanserv
, channel
, &change
);
2635 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2639 static CHANSERV_FUNC(cmd_adduser
)
2641 struct userData
*actee
;
2642 struct userData
*actor
;
2643 struct handle_info
*handle
;
2644 unsigned short access
;
2648 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2650 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2654 access
= user_level_from_name(argv
[2], UL_OWNER
);
2657 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2661 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2662 if(actor
->access
<= access
)
2664 reply("CSMSG_NO_BUMP_ACCESS");
2668 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2670 // 'kevin must first authenticate with AuthServ.' is sent to user
2671 struct userNode
*unode
;
2672 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2675 if(find_adduser_pending(channel
, unode
)) {
2676 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2679 if(IsInChannel(channel
, unode
)) {
2680 reply("CSMSG_ADDUSER_PENDING");
2681 add_adduser_pending(channel
, unode
, access
);
2682 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2684 /* this results in user must auth AND not in chan errors. too confusing..
2686 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2694 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2696 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2701 unsigned int duration
= 0;
2703 if ((duration
= ParseInterval(argv
[3])))
2704 expiry
= now
+ duration
;
2707 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
, expiry
);
2708 scan_user_presence(actee
, NULL
);
2711 timeq_add(expiry
, chanserv_expire_tempuser
, actee
);
2713 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2717 static CHANSERV_FUNC(cmd_clvl
)
2719 struct handle_info
*handle
;
2720 struct userData
*victim
;
2721 struct userData
*actor
;
2722 unsigned short new_access
;
2723 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2727 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2729 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2732 if(handle
== user
->handle_info
&& !privileged
)
2734 reply("CSMSG_NO_SELF_CLVL");
2738 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2740 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2744 if(actor
->access
<= victim
->access
&& !privileged
)
2746 reply("MSG_USER_OUTRANKED", handle
->handle
);
2750 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2754 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2758 if(new_access
>= actor
->access
&& !privileged
)
2760 reply("CSMSG_NO_BUMP_ACCESS");
2764 victim
->access
= new_access
;
2765 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2769 static CHANSERV_FUNC(cmd_deluser
)
2771 struct handle_info
*handle
;
2772 struct userData
*victim
;
2773 struct userData
*actor
;
2774 unsigned short access
;
2779 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2781 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2784 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2786 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2792 access
= user_level_from_name(argv
[1], UL_OWNER
);
2795 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2798 if(access
!= victim
->access
)
2800 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2806 access
= victim
->access
;
2809 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2811 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2815 chan_name
= strdup(channel
->name
);
2816 del_channel_user(victim
, 1);
2817 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2823 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2825 struct userData
*actor
, *uData
, *next
;
2827 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2829 if(min_access
> max_access
)
2831 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2835 if((actor
->access
<= max_access
) && !IsHelping(user
))
2837 reply("CSMSG_NO_ACCESS");
2841 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2845 if((uData
->access
>= min_access
)
2846 && (uData
->access
<= max_access
)
2847 && match_ircglob(uData
->handle
->handle
, mask
))
2848 del_channel_user(uData
, 1);
2851 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2855 static CHANSERV_FUNC(cmd_mdelowner
)
2857 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2860 static CHANSERV_FUNC(cmd_mdelcoowner
)
2862 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
2865 static CHANSERV_FUNC(cmd_mdelmanager
)
2867 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
2870 static CHANSERV_FUNC(cmd_mdelop
)
2872 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
2875 static CHANSERV_FUNC(cmd_mdelhalfop
)
2877 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
2880 static CHANSERV_FUNC(cmd_mdelpeon
)
2882 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
2886 static CHANSERV_FUNC(cmd_levels
)
2888 struct helpfile_table tbl
;
2891 tbl
.length
= 6 + 1; // 6 levels
2894 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
2895 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
2896 tbl
.contents
[0][0] = "Level";
2897 tbl
.contents
[0][1] = "From";
2898 tbl
.contents
[0][2] = "-";
2899 tbl
.contents
[0][3] = "To";
2901 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2902 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
2903 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
2904 tbl
.contents
[ii
][2] = msnprintf(2, " ");
2905 tbl
.contents
[ii
][3] = msnprintf(1, "");
2907 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2908 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
2909 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
2910 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2911 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
2913 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2914 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
2915 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
2916 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2917 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
2919 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2920 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
2921 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
2922 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2923 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
2925 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2926 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
2927 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
2928 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2929 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
2931 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2932 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
2933 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
2934 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2935 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
2937 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
2941 reply("CSMSG_LEVELS_HEADER");
2942 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
2943 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
2944 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
2945 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
2946 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
2947 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
2954 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2956 struct banData
*bData
, *next
;
2957 char interval
[INTERVALLEN
];
2962 limit
= now
- duration
;
2963 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2967 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2970 del_channel_ban(bData
);
2974 intervalString(interval
, duration
, user
->handle_info
);
2975 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2980 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
)
2982 struct userData
*actor
, *uData
, *next
;
2983 char interval
[INTERVALLEN
];
2987 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2988 if(min_access
> max_access
)
2990 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2994 if((actor
->access
<= max_access
) && !IsHelping(user
))
2996 reply("CSMSG_NO_ACCESS");
3001 limit
= now
- duration
;
3002 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3006 if((uData
->seen
> limit
)
3008 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3011 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3012 || (!max_access
&& (uData
->access
< actor
->access
)))
3014 del_channel_user(uData
, 1);
3022 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3024 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3028 static CHANSERV_FUNC(cmd_trim
)
3030 unsigned long duration
;
3031 unsigned short min_level
, max_level
;
3036 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3037 duration
= ParseInterval(argv
[2]);
3040 reply("CSMSG_CANNOT_TRIM");
3044 if(!irccasecmp(argv
[1], "lamers"))
3046 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3049 else if(!irccasecmp(argv
[1], "users"))
3051 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3054 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3056 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3059 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3061 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3066 reply("CSMSG_INVALID_TRIM", argv
[1]);
3071 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3072 to the user. cmd_all takes advantage of this. */
3073 static CHANSERV_FUNC(cmd_up
)
3075 struct mod_chanmode change
;
3076 struct userData
*uData
;
3079 mod_chanmode_init(&change
);
3081 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3082 if(!change
.args
[0].u
.member
)
3085 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3089 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3093 reply("CSMSG_GODMODE_UP", argv
[0]);
3096 else if(uData
->access
>= UL_OP
)
3098 change
.args
[0].mode
= MODE_CHANOP
;
3099 errmsg
= "CSMSG_ALREADY_OPPED";
3101 else if(uData
->access
>= UL_HALFOP
)
3103 change
.args
[0].mode
= MODE_HALFOP
;
3104 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3106 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3108 change
.args
[0].mode
= MODE_VOICE
;
3109 errmsg
= "CSMSG_ALREADY_VOICED";
3114 reply("CSMSG_NO_ACCESS");
3117 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3118 if(!change
.args
[0].mode
)
3121 reply(errmsg
, channel
->name
);
3124 modcmd_chanmode_announce(&change
);
3128 static CHANSERV_FUNC(cmd_down
)
3130 struct mod_chanmode change
;
3132 mod_chanmode_init(&change
);
3134 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3135 if(!change
.args
[0].u
.member
)
3138 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3142 if(!change
.args
[0].u
.member
->modes
)
3145 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3149 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3150 modcmd_chanmode_announce(&change
);
3154 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
)
3156 struct userData
*cList
;
3158 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3160 if(IsSuspended(cList
->channel
)
3161 || IsUserSuspended(cList
)
3162 || !GetUserMode(cList
->channel
->channel
, user
))
3165 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3171 static CHANSERV_FUNC(cmd_upall
)
3173 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3176 static CHANSERV_FUNC(cmd_downall
)
3178 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3181 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3182 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3185 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
)
3187 unsigned int ii
, valid
;
3188 struct userNode
*victim
;
3189 struct mod_chanmode
*change
;
3191 change
= mod_chanmode_alloc(argc
- 1);
3193 for(ii
=valid
=0; ++ii
< argc
; )
3195 if(!(victim
= GetUserH(argv
[ii
])))
3197 change
->args
[valid
].mode
= mode
;
3198 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3199 if(!change
->args
[valid
].u
.member
)
3201 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3206 change
->argc
= valid
;
3207 if(valid
< (argc
-1))
3208 reply("CSMSG_PROCESS_FAILED");
3211 modcmd_chanmode_announce(change
);
3212 reply(action
, channel
->name
);
3214 mod_chanmode_free(change
);
3218 static CHANSERV_FUNC(cmd_op
)
3220 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3223 static CHANSERV_FUNC(cmd_hop
)
3225 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3228 static CHANSERV_FUNC(cmd_deop
)
3230 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3233 static CHANSERV_FUNC(cmd_dehop
)
3235 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3238 static CHANSERV_FUNC(cmd_voice
)
3240 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3243 static CHANSERV_FUNC(cmd_devoice
)
3245 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3249 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3255 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3257 struct modeNode
*mn
= channel
->members
.list
[ii
];
3259 if(IsService(mn
->user
))
3262 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3265 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3269 victims
[(*victimCount
)++] = mn
;
3275 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3277 struct userNode
*victim
;
3278 struct modeNode
**victims
;
3279 unsigned int offset
, n
, victimCount
, duration
= 0;
3280 char *reason
= "Bye.", *ban
, *name
;
3281 char interval
[INTERVALLEN
];
3283 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3284 REQUIRE_PARAMS(offset
);
3287 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3288 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3290 /* Truncate the reason to a length of TOPICLEN, as
3291 the ircd does; however, leave room for an ellipsis
3292 and the kicker's nick. */
3293 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3297 if((victim
= GetUserH(argv
[1])))
3299 victims
= alloca(sizeof(victims
[0]));
3300 victims
[0] = GetUserMode(channel
, victim
);
3301 /* XXX: The comparison with ACTION_KICK is just because all
3302 * other actions can work on users outside the channel, and we
3303 * want to allow those (e.g. unbans) in that case. If we add
3304 * some other ejection action for in-channel users, change
3306 victimCount
= victims
[0] ? 1 : 0;
3308 if(IsService(victim
))
3311 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3315 if((action
== ACTION_KICK
) && !victimCount
)
3318 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3322 if(protect_user(victim
, user
, channel
->channel_info
, false))
3324 // This translates to send_message(user, cmd->parent->bot, ...)
3325 // if user is x3 (ctcp action) cmd is null and segfault.
3327 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3331 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3332 name
= victim
->nick
;
3336 if(!is_ircmask(argv
[1]))
3339 reply("MSG_NICK_UNKNOWN", argv
[1]);
3343 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3345 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3348 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3351 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3352 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3354 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3355 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3356 some creativity, but its not x3's job to be the ban censor anyway. */
3357 if(is_overmask(argv
[1]))
3360 reply("CSMSG_LAME_MASK", argv
[1]);
3364 if((action
== ACTION_KICK
) && (victimCount
== 0))
3367 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3371 name
= ban
= strdup(argv
[1]);
3374 /* Truncate the ban in place if necessary; we must ensure
3375 that 'ban' is a valid ban mask before sanitizing it. */
3376 sanitize_ircmask(ban
);
3378 if(action
& ACTION_ADD_LAMER
)
3380 struct banData
*bData
, *next
;
3382 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3385 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3390 if(action
& ACTION_ADD_TIMED_LAMER
)
3392 duration
= ParseInterval(argv
[2]);
3397 reply("CSMSG_DURATION_TOO_LOW");
3401 else if(duration
> (86400 * 365 * 2))
3404 reply("CSMSG_DURATION_TOO_HIGH");
3411 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3413 if(match_ircglobs(bData
->mask
, ban
))
3415 int exact
= !irccasecmp(bData
->mask
, ban
);
3417 /* The ban is redundant; there is already a ban
3418 with the same effect in place. */
3422 free(bData
->reason
);
3423 bData
->reason
= strdup(reason
);
3424 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3426 reply("CSMSG_REASON_CHANGE", ban
);
3430 if(exact
&& bData
->expires
)
3434 /* If the ban matches an existing one exactly,
3435 extend the expiration time if the provided
3436 duration is longer. */
3437 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3439 bData
->expires
= now
+ duration
;
3450 /* Delete the expiration timeq entry and
3451 requeue if necessary. */
3452 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3455 timeq_add(bData
->expires
, expire_ban
, bData
);
3459 /* automated kickban, dont reply */
3462 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3464 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3470 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3477 if(match_ircglobs(ban
, bData
->mask
))
3479 /* The ban we are adding makes previously existing
3480 bans redundant; silently remove them. */
3481 del_channel_ban(bData
);
3485 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
);
3487 name
= ban
= strdup(bData
->mask
);
3491 /* WHAT DOES THIS DO?? -Rubin */
3492 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3494 extern const char *hidden_host_suffix
;
3495 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3497 unsigned int l1
, l2
;
3500 l2
= strlen(old_name
);
3503 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3505 new_mask
= malloc(MAXLEN
);
3506 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3508 name
= ban
= new_mask
;
3513 if(action
& ACTION_BAN
)
3515 unsigned int exists
;
3516 struct mod_chanmode
*change
;
3518 if(channel
->banlist
.used
>= MAXBANS
)
3521 reply("CSMSG_BANLIST_FULL", channel
->name
);
3526 exists
= ChannelBanExists(channel
, ban
);
3527 change
= mod_chanmode_alloc(victimCount
+ 1);
3528 for(n
= 0; n
< victimCount
; ++n
)
3530 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3531 change
->args
[n
].u
.member
= victims
[n
];
3535 change
->args
[n
].mode
= MODE_BAN
;
3536 change
->args
[n
++].u
.hostmask
= ban
;
3540 modcmd_chanmode_announce(change
);
3542 mod_chanmode_announce(chanserv
, channel
, change
);
3543 mod_chanmode_free(change
);
3545 if(exists
&& (action
== ACTION_BAN
))
3548 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3554 if(action
& ACTION_ADD_LAMER
)
3556 char kick_reason
[MAXLEN
];
3557 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3559 for(n
= 0; n
< victimCount
; n
++) {
3560 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3561 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3565 else if(action
& ACTION_KICK
)
3567 char kick_reason
[MAXLEN
];
3568 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3570 for(n
= 0; n
< victimCount
; n
++) {
3571 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3577 /* No response, since it was automated. */
3579 else if(action
& ACTION_ADD_LAMER
)
3582 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3584 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3586 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3587 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3588 else if(action
& ACTION_BAN
)
3589 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3590 else if(action
& ACTION_KICK
&& victimCount
)
3591 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3597 static CHANSERV_FUNC(cmd_kickban
)
3599 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3602 static CHANSERV_FUNC(cmd_kick
)
3604 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3607 static CHANSERV_FUNC(cmd_ban
)
3609 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3612 static CHANSERV_FUNC(cmd_addlamer
)
3614 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3617 static CHANSERV_FUNC(cmd_addtimedlamer
)
3619 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3622 static struct mod_chanmode
*
3623 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3625 struct mod_chanmode
*change
;
3626 unsigned char *match
;
3627 unsigned int ii
, count
;
3629 match
= alloca(bans
->used
);
3632 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3634 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3635 MATCH_USENICK
| MATCH_VISIBLE
);
3642 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3644 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3651 change
= mod_chanmode_alloc(count
);
3652 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3656 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3657 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3659 assert(count
== change
->argc
);
3663 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3665 unsigned int jj
, ii
, count
;
3667 struct chanData
*channel
;
3669 struct mod_chanmode
*change
;
3671 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3672 /* Walk through every channel */
3673 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3674 switch(channel
->chOpts
[chBanTimeout
])
3676 default: case '0': continue; /* Dont remove bans in this chan */
3677 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3678 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3679 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3680 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3681 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3684 /* First find out how many bans were going to unset */
3685 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3686 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3690 /* At least one ban, so setup a removal */
3691 change
= mod_chanmode_alloc(count
);
3693 /* Walk over every ban in this channel.. */
3694 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3695 bn
= channel
->channel
->banlist
.list
[jj
];
3696 if (bn
->set
< bantimeout
) {
3697 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3699 /* Add this ban to the mode change */
3700 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3701 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3703 /* Pull this ban out of the list */
3704 banList_remove(&(channel
->channel
->banlist
), bn
);
3709 /* Send the modes to IRC */
3710 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3712 /* free memory from strdup above */
3713 for(ii
= 0; ii
< count
; ++ii
)
3714 free((char*)change
->args
[ii
].u
.hostmask
);
3716 mod_chanmode_free(change
);
3719 /* Set this function to run again */
3720 if(chanserv_conf
.ban_timeout_frequency
)
3721 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3726 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3728 struct userNode
*actee
;
3734 /* may want to allow a comma delimited list of users... */
3735 if(!(actee
= GetUserH(argv
[1])))
3737 if(!is_ircmask(argv
[1]))
3739 reply("MSG_NICK_UNKNOWN", argv
[1]);
3743 mask
= strdup(argv
[1]);
3746 /* We don't sanitize the mask here because ircu
3748 if(action
& ACTION_UNBAN
)
3750 struct mod_chanmode
*change
;
3751 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3756 modcmd_chanmode_announce(change
);
3757 for(ii
= 0; ii
< change
->argc
; ++ii
)
3758 free((char*)change
->args
[ii
].u
.hostmask
);
3759 mod_chanmode_free(change
);
3764 if(action
& ACTION_DEL_LAMER
)
3766 struct banData
*ban
, *next
;
3768 ban
= channel
->channel_info
->bans
; /* lamers */
3772 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3775 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3780 del_channel_ban(ban
);
3787 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3789 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3795 static CHANSERV_FUNC(cmd_unban
)
3797 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3800 static CHANSERV_FUNC(cmd_dellamer
)
3802 /* it doesn't necessarily have to remove the channel ban - may want
3803 to make that an option. */
3804 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3807 static CHANSERV_FUNC(cmd_unbanme
)
3809 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3810 long flags
= ACTION_UNBAN
;
3812 /* remove permanent bans if the user has the proper access. */
3813 if(uData
->access
>= UL_MANAGER
)
3814 flags
|= ACTION_DEL_LAMER
;
3816 argv
[1] = user
->nick
;
3817 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3820 static CHANSERV_FUNC(cmd_unbanall
)
3822 struct mod_chanmode
*change
;
3825 if(!channel
->banlist
.used
)
3827 reply("CSMSG_NO_BANS", channel
->name
);
3831 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3832 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3834 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3835 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3837 modcmd_chanmode_announce(change
);
3838 for(ii
= 0; ii
< change
->argc
; ++ii
)
3839 free((char*)change
->args
[ii
].u
.hostmask
);
3840 mod_chanmode_free(change
);
3841 reply("CSMSG_BANS_REMOVED", channel
->name
);
3845 static CHANSERV_FUNC(cmd_open
)
3847 struct mod_chanmode
*change
;
3850 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3852 change
= mod_chanmode_alloc(0);
3853 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3854 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3855 && channel
->channel_info
->modes
.modes_set
)
3856 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3857 modcmd_chanmode_announce(change
);
3858 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3859 for(ii
= 0; ii
< change
->argc
; ++ii
)
3860 free((char*)change
->args
[ii
].u
.hostmask
);
3861 mod_chanmode_free(change
);
3865 static CHANSERV_FUNC(cmd_myaccess
)
3867 static struct string_buffer sbuf
;
3868 struct handle_info
*target_handle
;
3869 struct userData
*uData
;
3872 target_handle
= user
->handle_info
;
3873 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3875 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
3877 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3880 if(!target_handle
->channels
)
3882 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3886 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3887 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3889 struct chanData
*cData
= uData
->channel
;
3891 if(uData
->access
> UL_OWNER
)
3893 if(IsProtected(cData
)
3894 && (target_handle
!= user
->handle_info
)
3895 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3898 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3899 if(uData
->flags
== USER_AUTO_OP
)
3900 string_buffer_append(&sbuf
, ',');
3901 if(IsUserSuspended(uData
))
3902 string_buffer_append(&sbuf
, 's');
3903 if(IsUserAutoOp(uData
))
3905 if(uData
->access
>= UL_OP
)
3906 string_buffer_append(&sbuf
, 'o');
3907 else if(uData
->access
>= UL_HALFOP
)
3908 string_buffer_append(&sbuf
, 'h');
3909 else if(uData
->access
>= UL_PEON
)
3910 string_buffer_append(&sbuf
, 'v');
3912 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3913 string_buffer_append(&sbuf
, 'i');
3914 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3915 string_buffer_append(&sbuf
, 'j');
3917 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3919 string_buffer_append_string(&sbuf
, ")]");
3920 string_buffer_append(&sbuf
, '\0');
3921 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3927 static CHANSERV_FUNC(cmd_access
)
3929 struct userNode
*target
;
3930 struct handle_info
*target_handle
;
3931 struct userData
*uData
;
3933 char prefix
[MAXLEN
];
3938 target_handle
= target
->handle_info
;
3940 else if((target
= GetUserH(argv
[1])))
3942 target_handle
= target
->handle_info
;
3944 else if(argv
[1][0] == '*')
3946 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3948 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3954 reply("MSG_NICK_UNKNOWN", argv
[1]);
3958 assert(target
|| target_handle
);
3960 if(target
== chanserv
)
3962 reply("CSMSG_IS_CHANSERV");
3970 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3975 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3978 reply("MSG_AUTHENTICATE");
3984 const char *epithet
= NULL
, *type
= NULL
;
3987 epithet
= chanserv_conf
.irc_operator_epithet
;
3990 else if(IsNetworkHelper(target
))
3992 epithet
= chanserv_conf
.network_helper_epithet
;
3993 type
= "network helper";
3995 else if(IsSupportHelper(target
))
3997 epithet
= chanserv_conf
.support_helper_epithet
;
3998 type
= "support helper";
4002 if(target_handle
->epithet
)
4003 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4005 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4007 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4011 sprintf(prefix
, "%s", target_handle
->handle
);
4014 if(!channel
->channel_info
)
4016 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4020 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4021 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4022 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4024 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4025 /* To prevent possible information leaks, only show infolines
4026 * if the requestor is in the channel or it's their own
4028 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4030 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4032 /* Likewise, only say it's suspended if the user has active
4033 * access in that channel or it's their own entry. */
4034 if(IsUserSuspended(uData
)
4035 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4036 || (user
->handle_info
== uData
->handle
)))
4038 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4043 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4049 /* This is never used...
4051 zoot_list(struct listData *list)
4053 struct userData *uData;
4054 unsigned int start, curr, highest, lowest;
4055 struct helpfile_table tmp_table;
4056 const char **temp, *msg;
4058 if(list->table.length == 1)
4061 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);
4063 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));
4064 msg = user_find_message(list->user, "MSG_NONE");
4065 send_message_type(4, list->user, list->bot, " %s", msg);
4067 tmp_table.width = list->table.width;
4068 tmp_table.flags = list->table.flags;
4069 list->table.contents[0][0] = " ";
4070 highest = list->highest;
4071 if(list->lowest != 0)
4072 lowest = list->lowest;
4073 else if(highest < 100)
4076 lowest = highest - 100;
4077 for(start = curr = 1; curr < list->table.length; )
4079 uData = list->users[curr-1];
4080 list->table.contents[curr++][0] = " ";
4081 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4084 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);
4086 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));
4087 temp = list->table.contents[--start];
4088 list->table.contents[start] = list->table.contents[0];
4089 tmp_table.contents = list->table.contents + start;
4090 tmp_table.length = curr - start;
4091 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4092 list->table.contents[start] = temp;
4094 highest = lowest - 1;
4095 lowest = (highest < 100) ? 0 : (highest - 99);
4102 normal_list(struct listData
*list
)
4106 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
);
4108 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
));
4109 if(list
->table
.length
== 1)
4111 msg
= user_find_message(list
->user
, "MSG_NONE");
4112 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4115 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4118 /* if these need changed, uncomment and customize
4120 clean_list(struct listData *list)
4124 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);
4126 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));
4127 if(list->table.length == 1)
4129 msg = user_find_message(list->user, "MSG_NONE");
4130 send_message_type(4, list->user, list->bot, " %s", msg);
4133 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4137 advanced_list(struct listData *list)
4141 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);
4143 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));
4144 if(list->table.length == 1)
4146 msg = user_find_message(list->user, "MSG_NONE");
4147 send_message_type(4, list->user, list->bot, " %s", msg);
4150 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4154 classic_list(struct listData *list)
4158 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4160 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4161 if(list->table.length == 1)
4163 msg = user_find_message(list->user, "MSG_NONE");
4164 send_message_type(4, list->user, list->bot, " %s", msg);
4167 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4172 userData_access_comp(const void *arg_a
, const void *arg_b
)
4174 const struct userData
*a
= *(struct userData
**)arg_a
;
4175 const struct userData
*b
= *(struct userData
**)arg_b
;
4177 if(a
->access
!= b
->access
)
4178 res
= b
->access
- a
->access
;
4180 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4185 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4187 void (*send_list
)(struct listData
*);
4188 struct userData
*uData
;
4189 struct listData lData
;
4190 unsigned int matches
;
4196 lData
.bot
= cmd
->parent
->bot
;
4197 lData
.channel
= channel
;
4198 lData
.lowest
= lowest
;
4199 lData
.highest
= highest
;
4200 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4201 send_list
= normal_list
;
4202 /* What does the following line do exactly?? */
4203 /*(void)zoot_list; ** since it doesn't show user levels */
4206 if(user->handle_info)
4208 switch(user->handle_info->userlist_style)
4210 case HI_STYLE_CLEAN:
4211 send_list = clean_list;
4213 case HI_STYLE_ADVANCED:
4214 send_list = advanced_list;
4216 case HI_STYLE_CLASSIC:
4217 send_list = classic_list;
4219 case HI_STYLE_NORMAL:
4221 send_list = normal_list;
4226 send_list
= normal_list
;
4228 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4230 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4232 if((uData
->access
< lowest
)
4233 || (uData
->access
> highest
)
4234 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4236 lData
.users
[matches
++] = uData
;
4238 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4240 lData
.table
.length
= matches
+1;
4241 lData
.table
.flags
= TABLE_NO_FREE
;
4242 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4244 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4245 lData
.table
.width
= 6; /* with level = 6 */
4247 lData
.table
.width
= 5; /* without = 5 */
4248 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4249 lData
.table
.contents
[0] = ary
;
4250 if(user
->handle_info
) {
4251 switch(user
->handle_info
->userlist_style
) {
4252 case HI_STYLE_CLASSIC
:
4255 case HI_STYLE_ADVANCED
:
4256 ary
[i
++] = "Access";
4259 case HI_STYLE_CLEAN
:
4260 ary
[i
++] = "Access";
4262 case HI_STYLE_NORMAL
:
4264 ary
[i
++] = "Access";
4269 ary
[i
++] = "Access";
4271 ary
[i
++] = "Account";
4272 ary
[i
] = "Last Seen";
4274 ary
[i
++] = "Status";
4275 ary
[i
++] = "Expiry";
4276 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4278 struct userData
*uData
= lData
.users
[matches
-1];
4279 char seen
[INTERVALLEN
];
4282 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4283 lData
.table
.contents
[matches
] = ary
;
4284 if(user
->handle_info
) {
4285 switch(user
->handle_info
->userlist_style
) {
4286 case HI_STYLE_CLASSIC
:
4287 ary
[i
++] = strtab(uData
->access
);
4289 case HI_STYLE_ADVANCED
:
4290 ary
[i
++] = user_level_name_from_level(uData
->access
);
4291 ary
[i
++] = strtab(uData
->access
);
4293 case HI_STYLE_CLEAN
:
4294 ary
[i
++] = user_level_name_from_level(uData
->access
);
4296 case HI_STYLE_NORMAL
:
4298 ary
[i
++] = user_level_name_from_level(uData
->access
);
4303 ary
[i
++] = user_level_name_from_level(uData
->access
);
4305 ary
[i
++] = uData
->handle
->handle
;
4308 else if(!uData
->seen
)
4311 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4312 ary
[i
] = strdup(ary
[i
]);
4314 if(IsUserSuspended(uData
))
4315 ary
[i
++] = "Suspended";
4316 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4317 ary
[i
++] = "Vacation";
4319 ary
[i
++] = "Normal";
4321 if (uData
->expiry
> 0) {
4322 char delay
[INTERVALLEN
];
4325 diff
= uData
->expiry
- now
;
4326 intervalString(delay
, diff
, user
->handle_info
);
4333 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4335 /* Free strdup above */
4336 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4337 free(lData
.table
.contents
[matches
]);
4339 free(lData
.table
.contents
[0]);
4340 free(lData
.table
.contents
);
4344 /* Remove this now that debugging is over? or improve it for
4345 * users? Would it be better tied into USERS somehow? -Rubin */
4346 static CHANSERV_FUNC(cmd_pending
)
4348 struct adduserPending
*ap
;
4349 reply("CSMSG_ADDUSER_PENDING_HEADER");
4350 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4352 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4353 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4354 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4358 static CHANSERV_FUNC(cmd_users
)
4360 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4363 static CHANSERV_FUNC(cmd_wlist
)
4365 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4368 static CHANSERV_FUNC(cmd_clist
)
4370 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4373 static CHANSERV_FUNC(cmd_mlist
)
4375 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4378 static CHANSERV_FUNC(cmd_olist
)
4380 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4383 static CHANSERV_FUNC(cmd_hlist
)
4385 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4388 static CHANSERV_FUNC(cmd_plist
)
4390 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4393 static CHANSERV_FUNC(cmd_lamers
)
4395 struct helpfile_table tbl
;
4396 unsigned int matches
= 0, timed
= 0, ii
;
4397 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4398 const char *msg_never
, *triggered
, *expires
;
4399 struct banData
*ban
, **bans
; /* lamers */
4406 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4407 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4410 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4412 if(search
&& !match_ircglobs(search
, ban
->mask
))
4414 bans
[matches
++] = ban
;
4419 tbl
.length
= matches
+ 1;
4420 tbl
.width
= 4 + timed
;
4422 tbl
.flags
= TABLE_NO_FREE
;
4423 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4424 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4425 tbl
.contents
[0][0] = "Mask";
4426 tbl
.contents
[0][1] = "Set By";
4427 tbl
.contents
[0][2] = "Triggered";
4430 tbl
.contents
[0][3] = "Expires";
4431 tbl
.contents
[0][4] = "Reason";
4434 tbl
.contents
[0][3] = "Reason";
4437 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4438 /* reply("MSG_NONE"); */
4439 free(tbl
.contents
[0]);
4444 msg_never
= user_find_message(user
, "MSG_NEVER");
4445 for(ii
= 0; ii
< matches
; )
4451 else if(ban
->expires
)
4452 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4454 expires
= msg_never
;
4457 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4459 triggered
= msg_never
;
4461 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4462 tbl
.contents
[ii
][0] = ban
->mask
;
4463 tbl
.contents
[ii
][1] = ban
->owner
;
4464 tbl
.contents
[ii
][2] = strdup(triggered
);
4467 tbl
.contents
[ii
][3] = strdup(expires
);
4468 tbl
.contents
[ii
][4] = ban
->reason
;
4471 tbl
.contents
[ii
][3] = ban
->reason
;
4473 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4474 /* reply("MSG_MATCH_COUNT", matches); */
4475 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4477 free((char*)tbl
.contents
[ii
][2]);
4479 free((char*)tbl
.contents
[ii
][3]);
4480 free(tbl
.contents
[ii
]);
4482 free(tbl
.contents
[0]);
4489 * return + if the user does NOT have the right to set the topic, and
4490 * the topic is changed.
4493 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4495 struct chanData
*cData
= channel
->channel_info
;
4496 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4498 else if(cData
->topic
)
4499 return irccasecmp(new_topic
, cData
->topic
);
4506 * Makes a givin topic fit into a givin topic mask and returns
4509 * topic_mask - the mask to conform to
4510 * topic - the topic to make conform
4511 * new_topic - the pre-allocated char* to put the new topic into
4513 * modifies: new_topic
4516 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4518 //char *topic_mask = cData->topic_mask;
4520 int pos
=0, starpos
=-1, dpos
=0, len
;
4522 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4529 strcpy(new_topic
, "");
4532 len
= strlen(topic
);
4533 if((dpos
+ len
) > TOPICLEN
)
4534 len
= TOPICLEN
+ 1 - dpos
;
4535 memcpy(new_topic
+dpos
, topic
, len
);
4539 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4540 default: new_topic
[dpos
++] = tchar
; break;
4543 if((dpos
> TOPICLEN
) || tchar
)
4545 strcpy(new_topic
, "");
4548 new_topic
[dpos
] = 0;
4552 static CHANSERV_FUNC(cmd_topic
)
4554 struct chanData
*cData
;
4558 #ifdef WITH_PROTOCOL_P10
4562 cData
= channel
->channel_info
;
4567 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4568 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4569 reply("CSMSG_TOPIC_SET", cData
->topic
);
4573 reply("CSMSG_NO_TOPIC", channel
->name
);
4577 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4578 /* If they say "!topic *", use an empty topic. */
4579 if((topic
[0] == '*') && (topic
[1] == 0))
4582 if(bad_topic(channel
, user
, topic
))
4584 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4589 /* If there is a topicmask set, and the new topic doesnt match, make it */
4590 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4592 char *topic_mask
= cData
->topic_mask
;
4593 char new_topic
[TOPICLEN
+1];
4595 /* make a new topic fitting mask */
4596 conform_topic(topic_mask
, topic
, new_topic
);
4599 /* Topic couldnt fit into mask, was too long */
4600 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4601 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4604 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4606 else /* No mask set, just set the topic */
4607 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4610 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4612 /* Grab the topic and save it as the default topic. */
4614 cData
->topic
= strdup(channel
->topic
);
4620 static CHANSERV_FUNC(cmd_mode
)
4622 struct userData
*uData
;
4623 struct mod_chanmode
*change
;
4628 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4629 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4633 change
= &channel
->channel_info
->modes
;
4634 if(change
->modes_set
|| change
->modes_clear
) {
4635 modcmd_chanmode_announce(change
);
4636 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4638 reply("CSMSG_NO_MODES", channel
->name
);
4642 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4644 base_oplevel
= MAXOPLEVEL
;
4645 else if (uData
->access
>= UL_OWNER
)
4648 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4649 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4653 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4657 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4658 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4661 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4662 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4666 modcmd_chanmode_announce(change
);
4667 mod_chanmode_free(change
);
4668 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4672 static CHANSERV_FUNC(cmd_invite
)
4674 struct userData
*uData
;
4675 struct userNode
*invite
;
4677 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4681 if(!(invite
= GetUserH(argv
[1])))
4683 reply("MSG_NICK_UNKNOWN", argv
[1]);
4690 if(GetUserMode(channel
, invite
))
4692 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4700 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4701 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4704 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4707 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4709 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4710 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4711 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4717 irc_invite(chanserv
, invite
, channel
);
4719 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4724 static CHANSERV_FUNC(cmd_inviteme
)
4726 if(GetUserMode(channel
, user
))
4728 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4731 if(channel
->channel_info
4732 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4734 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4737 irc_invite(cmd
->parent
->bot
, user
, channel
);
4742 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4745 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4747 /* We display things based on two dimensions:
4748 * - Issue time: present or absent
4749 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4750 * (in order of precedence, so something both expired and revoked
4751 * only counts as revoked)
4753 combo
= (suspended
->issued
? 4 : 0)
4754 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4756 case 0: /* no issue time, indefinite expiration */
4757 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4759 case 1: /* no issue time, expires in future */
4760 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4761 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4763 case 2: /* no issue time, expired */
4764 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4765 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4767 case 3: /* no issue time, revoked */
4768 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4769 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4771 case 4: /* issue time set, indefinite expiration */
4772 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4773 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4775 case 5: /* issue time set, expires in future */
4776 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4777 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4778 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4780 case 6: /* issue time set, expired */
4781 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4782 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4783 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4785 case 7: /* issue time set, revoked */
4786 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4787 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4788 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4791 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4797 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4800 const char *fmt
= "%a %b %d %H:%M %Y";
4801 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4803 if(giveownership
->staff_issuer
)
4805 if(giveownership
->reason
)
4806 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4807 giveownership
->target
, giveownership
->target_access
,
4808 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4810 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4811 giveownership
->target
, giveownership
->target_access
,
4812 giveownership
->staff_issuer
, buf
);
4816 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4821 static CHANSERV_FUNC(cmd_info
)
4823 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4824 struct userData
*uData
, *owner
;
4825 struct chanData
*cData
;
4826 struct do_not_register
*dnr
;
4831 cData
= channel
->channel_info
;
4832 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4833 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4836 uData
= GetChannelUser(cData
, user
->handle_info
);
4837 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4839 mod_chanmode_format(&cData
->modes
, modes
);
4840 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4841 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4844 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4848 note
= iter_data(it
);
4849 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4852 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4853 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4856 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4857 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4858 if(owner
->access
== UL_OWNER
)
4859 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4860 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4861 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4862 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4864 privileged
= IsStaff(user
);
4865 /* if(privileged) */
4866 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4867 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4868 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4870 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4871 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4873 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4875 struct suspended
*suspended
;
4876 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4877 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4878 show_suspension_info(cmd
, user
, suspended
);
4880 else if(IsSuspended(cData
))
4882 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4883 show_suspension_info(cmd
, user
, cData
->suspended
);
4885 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4887 struct giveownership
*giveownership
;
4888 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4889 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4890 show_giveownership_info(cmd
, user
, giveownership
);
4892 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4893 reply("CSMSG_CHANNEL_END");
4895 reply("CSMSG_CHANNEL_END_CLEAN");
4899 static CHANSERV_FUNC(cmd_netinfo
)
4901 extern time_t boot_time
;
4902 extern unsigned long burst_length
;
4903 char interval
[INTERVALLEN
];
4905 reply("CSMSG_NETWORK_INFO");
4906 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4907 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4908 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4909 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4910 reply("CSMSG_NETWORK_LAMERS", banCount
);
4911 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4912 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4913 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4918 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4920 struct helpfile_table table
;
4922 struct userNode
*user
;
4927 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4928 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4929 for(nn
=0; nn
<list
->used
; nn
++)
4931 user
= list
->list
[nn
];
4932 if(user
->modes
& skip_flags
)
4936 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4939 nick
= alloca(strlen(user
->nick
)+3);
4940 sprintf(nick
, "(%s)", user
->nick
);
4944 table
.contents
[table
.length
][0] = nick
;
4947 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4950 static CHANSERV_FUNC(cmd_ircops
)
4952 reply("CSMSG_STAFF_OPERS");
4953 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4957 static CHANSERV_FUNC(cmd_helpers
)
4959 reply("CSMSG_STAFF_HELPERS");
4960 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4964 static CHANSERV_FUNC(cmd_staff
)
4966 reply("CSMSG_NETWORK_STAFF");
4967 cmd_ircops(CSFUNC_ARGS
);
4968 cmd_helpers(CSFUNC_ARGS
);
4972 static CHANSERV_FUNC(cmd_peek
)
4974 struct modeNode
*mn
;
4975 char modes
[MODELEN
];
4977 struct helpfile_table table
;
4979 irc_make_chanmode(channel
, modes
);
4981 reply("CSMSG_PEEK_INFO", channel
->name
);
4982 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4984 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4985 reply("CSMSG_PEEK_MODES", modes
);
4986 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4990 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4991 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4992 for(n
= 0; n
< channel
->members
.used
; n
++)
4994 mn
= channel
->members
.list
[n
];
4995 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4997 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4998 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5003 reply("CSMSG_PEEK_OPS");
5004 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5007 reply("CSMSG_PEEK_NO_OPS");
5008 reply("CSMSG_PEEK_END");
5012 static MODCMD_FUNC(cmd_wipeinfo
)
5014 struct handle_info
*victim
;
5015 struct userData
*ud
, *actor
;
5018 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5019 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5021 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5023 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5026 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5028 reply("MSG_USER_OUTRANKED", victim
->handle
);
5034 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5039 resync_channel(struct chanNode
*channel
)
5041 struct mod_chanmode
*changes
;
5042 struct chanData
*cData
= channel
->channel_info
;
5043 unsigned int ii
, used
;
5045 /* 6 = worst case -ovh+ovh on everyone */
5046 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5047 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5049 struct modeNode
*mn
= channel
->members
.list
[ii
];
5050 struct userData
*uData
;
5052 if(IsService(mn
->user
))
5056 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5058 /* If the channel is in no-mode mode, de-mode EVERYONE */
5059 if(cData
->chOpts
[chAutomode
] == 'n')
5063 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5064 changes
->args
[used
++].u
.member
= mn
;
5067 else /* Give various userlevels their modes.. */
5069 if(uData
&& uData
->access
>= UL_OP
)
5071 if(!(mn
->modes
& MODE_CHANOP
))
5073 changes
->args
[used
].mode
= MODE_CHANOP
;
5074 changes
->args
[used
++].u
.member
= mn
;
5077 else if(uData
&& uData
->access
>= UL_HALFOP
)
5079 if(mn
->modes
& MODE_CHANOP
)
5081 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5082 changes
->args
[used
++].u
.member
= mn
;
5084 if(!(mn
->modes
& MODE_HALFOP
))
5086 changes
->args
[used
].mode
= MODE_HALFOP
;
5087 changes
->args
[used
++].u
.member
= mn
;
5090 else if(uData
&& uData
->access
>= UL_PEON
)
5092 if(mn
->modes
& MODE_CHANOP
)
5094 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5095 changes
->args
[used
++].u
.member
= mn
;
5097 if(mn
->modes
& MODE_HALFOP
)
5099 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5100 changes
->args
[used
++].u
.member
= mn
;
5102 /* Don't voice peons if were in mode m */
5103 if( cData
->chOpts
[chAutomode
] == 'm')
5105 if(mn
->modes
& MODE_VOICE
)
5107 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5108 changes
->args
[used
++].u
.member
= mn
;
5111 /* otherwise, make user they do have voice */
5112 else if(!(mn
->modes
& MODE_VOICE
))
5114 changes
->args
[used
].mode
= MODE_VOICE
;
5115 changes
->args
[used
++].u
.member
= mn
;
5118 else /* They arnt on the userlist.. */
5120 /* If we voice everyone, but they dont.. */
5121 if(cData
->chOpts
[chAutomode
] == 'v')
5123 /* Remove anything except v */
5124 if(mn
->modes
& ~MODE_VOICE
)
5126 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5127 changes
->args
[used
++].u
.member
= mn
;
5130 if(!(mn
->modes
& MODE_VOICE
))
5132 changes
->args
[used
].mode
= MODE_VOICE
;
5133 changes
->args
[used
++].u
.member
= mn
;
5136 /* If we hop everyone, but they dont.. */
5137 else if(cData
->chOpts
[chAutomode
] == 'h')
5139 /* Remove anything except h */
5140 if(mn
->modes
& ~MODE_HALFOP
)
5142 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5143 changes
->args
[used
++].u
.member
= mn
;
5146 if(!(mn
->modes
& MODE_HALFOP
))
5148 changes
->args
[used
].mode
= MODE_HALFOP
;
5149 changes
->args
[used
++].u
.member
= mn
;
5152 /* If we op everyone, but they dont.. */
5153 else if(cData
->chOpts
[chAutomode
] == 'o')
5155 /* Remove anything except h */
5156 if(mn
->modes
& ~MODE_CHANOP
)
5158 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5159 changes
->args
[used
++].u
.member
= mn
;
5162 if(!(mn
->modes
& MODE_CHANOP
))
5164 changes
->args
[used
].mode
= MODE_CHANOP
;
5165 changes
->args
[used
++].u
.member
= mn
;
5168 /* they have no excuse for having modes, de-everything them */
5173 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5174 changes
->args
[used
++].u
.member
= mn
;
5180 changes
->argc
= used
;
5181 mod_chanmode_announce(chanserv
, channel
, changes
);
5182 mod_chanmode_free(changes
);
5185 static CHANSERV_FUNC(cmd_resync
)
5187 resync_channel(channel
);
5188 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5192 static CHANSERV_FUNC(cmd_seen
)
5194 struct userData
*uData
;
5195 struct handle_info
*handle
;
5196 char seen
[INTERVALLEN
];
5200 if(!irccasecmp(argv
[1], chanserv
->nick
))
5202 reply("CSMSG_IS_CHANSERV");
5206 if(!(handle
= get_handle_info(argv
[1])))
5208 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5212 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5214 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5219 reply("CSMSG_USER_PRESENT", handle
->handle
);
5220 else if(uData
->seen
)
5221 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5223 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5225 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5226 reply("CSMSG_USER_VACATION", handle
->handle
);
5231 static MODCMD_FUNC(cmd_names
)
5233 struct userNode
*targ
;
5234 struct userData
*targData
;
5235 unsigned int ii
, pos
;
5238 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5240 targ
= channel
->members
.list
[ii
]->user
;
5241 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5244 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5247 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5251 if(IsUserSuspended(targData
))
5253 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5256 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5257 reply("CSMSG_END_NAMES", channel
->name
);
5262 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5264 switch(ntype
->visible_type
)
5266 case NOTE_VIS_ALL
: return 1;
5267 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5268 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5273 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5275 struct userData
*uData
;
5277 switch(ntype
->set_access_type
)
5279 case NOTE_SET_CHANNEL_ACCESS
:
5280 if(!user
->handle_info
)
5282 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5284 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5285 case NOTE_SET_CHANNEL_SETTER
:
5286 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5287 case NOTE_SET_PRIVILEGED
: default:
5288 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5292 static CHANSERV_FUNC(cmd_note
)
5294 struct chanData
*cData
;
5296 struct note_type
*ntype
;
5298 cData
= channel
->channel_info
;
5301 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5305 /* If no arguments, show all visible notes for the channel. */
5311 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5313 note
= iter_data(it
);
5314 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5317 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5318 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5321 reply("CSMSG_NOTELIST_END", channel
->name
);
5323 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5325 /* If one argument, show the named note. */
5328 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5329 && note_type_visible_to_user(cData
, note
->type
, user
))
5331 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5333 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5334 && note_type_visible_to_user(NULL
, ntype
, user
))
5336 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5341 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5345 /* Assume they're trying to set a note. */
5349 ntype
= dict_find(note_types
, argv
[1], NULL
);
5352 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5355 else if(note_type_settable_by_user(channel
, ntype
, user
))
5357 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5358 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5359 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5360 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5361 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5363 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5365 /* The note is viewable to staff only, so return 0
5366 to keep the invocation from getting logged (or
5367 regular users can see it in !events). */
5373 reply("CSMSG_NO_ACCESS");
5380 static CHANSERV_FUNC(cmd_delnote
)
5385 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5386 || !note_type_settable_by_user(channel
, note
->type
, user
))
5388 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5391 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5392 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5396 static CHANSERV_FUNC(cmd_last
)
5402 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5404 if(numoflines
< 1 || numoflines
> 200)
5406 reply("CSMSG_LAST_INVALID");
5409 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5413 static CHANSERV_FUNC(cmd_events
)
5415 struct logSearch discrim
;
5416 struct logReport report
;
5417 unsigned int matches
, limit
;
5419 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5420 if(limit
< 1 || limit
> 200)
5423 memset(&discrim
, 0, sizeof(discrim
));
5424 discrim
.masks
.bot
= chanserv
;
5425 discrim
.masks
.channel_name
= channel
->name
;
5427 discrim
.masks
.command
= argv
[2];
5428 discrim
.limit
= limit
;
5429 discrim
.max_time
= INT_MAX
;
5430 discrim
.severities
= 1 << LOG_COMMAND
;
5431 report
.reporter
= chanserv
;
5433 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5434 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5436 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5438 reply("MSG_MATCH_COUNT", matches
);
5440 reply("MSG_NO_MATCHES");
5444 static CHANSERV_FUNC(cmd_say
)
5450 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5451 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5453 else if(GetUserH(argv
[1]))
5456 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5457 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5461 reply("MSG_NOT_TARGET_NAME");
5467 static CHANSERV_FUNC(cmd_emote
)
5473 /* CTCP is so annoying. */
5474 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5475 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5477 else if(GetUserH(argv
[1]))
5479 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5480 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5484 reply("MSG_NOT_TARGET_NAME");
5490 struct channelList
*
5491 chanserv_support_channels(void)
5493 return &chanserv_conf
.support_channels
;
5496 static CHANSERV_FUNC(cmd_expire
)
5498 int channel_count
= registered_channels
;
5499 expire_channels(NULL
);
5500 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5505 chanserv_expire_suspension(void *data
)
5507 struct suspended
*suspended
= data
;
5508 struct chanNode
*channel
;
5510 if(!suspended
->expires
|| (now
< suspended
->expires
))
5511 suspended
->revoked
= now
;
5512 channel
= suspended
->cData
->channel
;
5513 suspended
->cData
->channel
= channel
;
5514 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5515 if(!IsOffChannel(suspended
->cData
))
5517 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5518 ss_cs_join_channel(channel
, 1);
5522 static CHANSERV_FUNC(cmd_csuspend
)
5524 struct suspended
*suspended
;
5525 char reason
[MAXLEN
];
5526 time_t expiry
, duration
;
5527 struct userData
*uData
;
5531 if(IsProtected(channel
->channel_info
))
5533 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5537 if(argv
[1][0] == '!')
5539 else if(IsSuspended(channel
->channel_info
))
5541 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5542 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5546 if(!strcmp(argv
[1], "0"))
5548 else if((duration
= ParseInterval(argv
[1])))
5549 expiry
= now
+ duration
;
5552 reply("MSG_INVALID_DURATION", argv
[1]);
5556 unsplit_string(argv
+ 2, argc
- 2, reason
);
5558 suspended
= calloc(1, sizeof(*suspended
));
5559 suspended
->revoked
= 0;
5560 suspended
->issued
= now
;
5561 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5562 suspended
->expires
= expiry
;
5563 suspended
->reason
= strdup(reason
);
5564 suspended
->cData
= channel
->channel_info
;
5565 suspended
->previous
= suspended
->cData
->suspended
;
5566 suspended
->cData
->suspended
= suspended
;
5568 if(suspended
->expires
)
5569 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5571 if(IsSuspended(channel
->channel_info
))
5573 suspended
->previous
->revoked
= now
;
5574 if(suspended
->previous
->expires
)
5575 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5577 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5578 channel
->name
, suspended
->suspender
);
5582 /* Mark all users in channel as absent. */
5583 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5592 /* Mark the channel as suspended, then part. */
5593 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5594 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5595 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5596 reply("CSMSG_SUSPENDED", channel
->name
);
5597 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5598 channel
->name
, suspended
->suspender
);
5603 static CHANSERV_FUNC(cmd_cunsuspend
)
5605 struct suspended
*suspended
;
5607 if(!IsSuspended(channel
->channel_info
))
5609 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5613 suspended
= channel
->channel_info
->suspended
;
5615 /* Expire the suspension and join ChanServ to the channel. */
5616 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5617 chanserv_expire_suspension(suspended
);
5618 reply("CSMSG_UNSUSPENDED", channel
->name
);
5619 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5620 channel
->name
, user
->handle_info
->handle
);
5624 typedef struct chanservSearch
5632 unsigned long flags
;
5636 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5639 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5644 search
= malloc(sizeof(struct chanservSearch
));
5645 memset(search
, 0, sizeof(*search
));
5648 for(i
= 0; i
< argc
; i
++)
5650 /* Assume all criteria require arguments. */
5653 reply("MSG_MISSING_PARAMS", argv
[i
]);
5657 if(!irccasecmp(argv
[i
], "name"))
5658 search
->name
= argv
[++i
];
5659 else if(!irccasecmp(argv
[i
], "registrar"))
5660 search
->registrar
= argv
[++i
];
5661 else if(!irccasecmp(argv
[i
], "unvisited"))
5662 search
->unvisited
= ParseInterval(argv
[++i
]);
5663 else if(!irccasecmp(argv
[i
], "registered"))
5664 search
->registered
= ParseInterval(argv
[++i
]);
5665 else if(!irccasecmp(argv
[i
], "flags"))
5668 if(!irccasecmp(argv
[i
], "nodelete"))
5669 search
->flags
|= CHANNEL_NODELETE
;
5670 else if(!irccasecmp(argv
[i
], "suspended"))
5671 search
->flags
|= CHANNEL_SUSPENDED
;
5674 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5678 else if(!irccasecmp(argv
[i
], "limit"))
5679 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5682 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5687 if(search
->name
&& !strcmp(search
->name
, "*"))
5689 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5690 search
->registrar
= 0;
5699 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5701 const char *name
= channel
->channel
->name
;
5702 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5703 (search
->registrar
&& !channel
->registrar
) ||
5704 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5705 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5706 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5707 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5714 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5716 struct chanData
*channel
;
5717 unsigned int matches
= 0;
5719 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5721 if(!chanserv_channel_match(channel
, search
))
5731 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5736 search_print(struct chanData
*channel
, void *data
)
5738 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5741 static CHANSERV_FUNC(cmd_search
)
5744 unsigned int matches
;
5745 channel_search_func action
;
5749 if(!irccasecmp(argv
[1], "count"))
5750 action
= search_count
;
5751 else if(!irccasecmp(argv
[1], "print"))
5752 action
= search_print
;
5755 reply("CSMSG_ACTION_INVALID", argv
[1]);
5759 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5763 if(action
== search_count
)
5764 search
->limit
= INT_MAX
;
5766 if(action
== search_print
)
5768 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5769 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5773 matches
= chanserv_channel_search(search
, action
, user
);
5776 reply("MSG_MATCH_COUNT", matches
);
5778 reply("MSG_NO_MATCHES");
5784 static CHANSERV_FUNC(cmd_unvisited
)
5786 struct chanData
*cData
;
5787 time_t interval
= chanserv_conf
.channel_expire_delay
;
5788 char buffer
[INTERVALLEN
];
5789 unsigned int limit
= 25, matches
= 0;
5793 interval
= ParseInterval(argv
[1]);
5795 limit
= atoi(argv
[2]);
5798 intervalString(buffer
, interval
, user
->handle_info
);
5799 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5801 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5803 if((now
- cData
->visited
) < interval
)
5806 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5807 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5814 static MODCMD_FUNC(chan_opt_defaulttopic
)
5820 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5822 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5826 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5828 free(channel
->channel_info
->topic
);
5829 if(topic
[0] == '*' && topic
[1] == 0)
5831 topic
= channel
->channel_info
->topic
= NULL
;
5835 topic
= channel
->channel_info
->topic
= strdup(topic
);
5836 if(channel
->channel_info
->topic_mask
5837 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5838 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5840 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5843 if(channel
->channel_info
->topic
)
5844 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5846 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5850 static MODCMD_FUNC(chan_opt_topicmask
)
5854 struct chanData
*cData
= channel
->channel_info
;
5857 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5859 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5863 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5865 if(cData
->topic_mask
)
5866 free(cData
->topic_mask
);
5867 if(mask
[0] == '*' && mask
[1] == 0)
5869 cData
->topic_mask
= 0;
5873 cData
->topic_mask
= strdup(mask
);
5875 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5876 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5877 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5881 if(channel
->channel_info
->topic_mask
)
5882 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5884 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5888 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5892 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5896 if(greeting
[0] == '*' && greeting
[1] == 0)
5900 unsigned int length
= strlen(greeting
);
5901 if(length
> chanserv_conf
.greeting_length
)
5903 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5906 *data
= strdup(greeting
);
5915 reply(name
, user_find_message(user
, "MSG_NONE"));
5919 static MODCMD_FUNC(chan_opt_greeting
)
5921 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5924 static MODCMD_FUNC(chan_opt_usergreeting
)
5926 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5929 static MODCMD_FUNC(chan_opt_modes
)
5931 struct mod_chanmode
*new_modes
;
5932 char modes
[MODELEN
];
5936 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5937 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5941 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5943 reply("CSMSG_NO_ACCESS");
5946 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5948 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5950 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5952 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5955 else if(new_modes
->argc
> 1)
5957 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5958 mod_chanmode_free(new_modes
);
5963 channel
->channel_info
->modes
= *new_modes
;
5964 modcmd_chanmode_announce(new_modes
);
5965 mod_chanmode_free(new_modes
);
5969 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5971 reply("CSMSG_SET_MODES", modes
);
5973 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5977 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5979 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5981 struct chanData
*cData
= channel
->channel_info
;
5986 /* Set flag according to value. */
5987 if(enabled_string(argv
[1]))
5989 cData
->flags
|= mask
;
5992 else if(disabled_string(argv
[1]))
5994 cData
->flags
&= ~mask
;
5999 reply("MSG_INVALID_BINARY", argv
[1]);
6005 /* Find current option value. */
6006 value
= (cData
->flags
& mask
) ? 1 : 0;
6010 reply(name
, user_find_message(user
, "MSG_ON"));
6012 reply(name
, user_find_message(user
, "MSG_OFF"));
6016 static MODCMD_FUNC(chan_opt_nodelete
)
6018 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6020 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6024 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6027 static MODCMD_FUNC(chan_opt_dynlimit
)
6029 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6032 static MODCMD_FUNC(chan_opt_offchannel
)
6034 struct chanData
*cData
= channel
->channel_info
;
6039 /* Set flag according to value. */
6040 if(enabled_string(argv
[1]))
6042 if(!IsOffChannel(cData
))
6043 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6044 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6047 else if(disabled_string(argv
[1]))
6049 if(IsOffChannel(cData
))
6051 struct mod_chanmode change
;
6052 mod_chanmode_init(&change
);
6054 change
.args
[0].mode
= MODE_CHANOP
;
6055 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6056 mod_chanmode_announce(chanserv
, channel
, &change
);
6058 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6063 reply("MSG_INVALID_BINARY", argv
[1]);
6069 /* Find current option value. */
6070 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6074 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6076 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6080 static MODCMD_FUNC(chan_opt_defaults
)
6082 struct userData
*uData
;
6083 struct chanData
*cData
;
6084 const char *confirm
;
6085 enum levelOption lvlOpt
;
6086 enum charOption chOpt
;
6088 cData
= channel
->channel_info
;
6089 uData
= GetChannelUser(cData
, user
->handle_info
);
6090 if(!uData
|| (uData
->access
< UL_OWNER
))
6092 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6095 confirm
= make_confirmation_string(uData
);
6096 if((argc
< 2) || strcmp(argv
[1], confirm
))
6098 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6101 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6102 cData
->modes
= chanserv_conf
.default_modes
;
6103 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6104 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6105 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6106 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6107 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6112 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6114 struct chanData
*cData
= channel
->channel_info
;
6115 struct userData
*uData
;
6116 unsigned short value
;
6120 if(!check_user_level(channel
, user
, option
, 1, 1))
6122 reply("CSMSG_CANNOT_SET");
6125 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6126 if(!value
&& strcmp(argv
[1], "0"))
6128 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6131 uData
= GetChannelUser(cData
, user
->handle_info
);
6132 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6134 reply("CSMSG_BAD_SETLEVEL");
6140 /* This test only applies to owners, since non-owners
6141 * trying to set an option to above their level get caught
6142 * by the CSMSG_BAD_SETLEVEL test above.
6144 if(value
> uData
->access
)
6146 reply("CSMSG_BAD_SETTERS");
6153 cData
->lvlOpts
[option
] = value
;
6155 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6159 static MODCMD_FUNC(chan_opt_enfops
)
6161 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6164 static MODCMD_FUNC(chan_opt_enfhalfops
)
6166 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6168 static MODCMD_FUNC(chan_opt_enfmodes
)
6170 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6173 static MODCMD_FUNC(chan_opt_enftopic
)
6175 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6178 static MODCMD_FUNC(chan_opt_pubcmd
)
6180 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6183 static MODCMD_FUNC(chan_opt_setters
)
6185 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6188 static MODCMD_FUNC(chan_opt_userinfo
)
6190 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6193 static MODCMD_FUNC(chan_opt_topicsnarf
)
6195 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6198 static MODCMD_FUNC(chan_opt_inviteme
)
6200 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6203 /* TODO: Make look like this when no args are
6205 * -X3- -------------------------------
6206 * -X3- BanTimeout: Bans are removed:
6207 * -X3- ----- * indicates current -----
6208 * -X3- 0: [*] Never.
6209 * -X3- 1: [ ] After 10 minutes.
6210 * -X3- 2: [ ] After 2 hours.
6211 * -X3- 3: [ ] After 4 hours.
6212 * -X3- 4: [ ] After 24 hours.
6213 * -X3- 5: [ ] After one week.
6214 * -X3- ------------- End -------------
6217 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6219 struct chanData
*cData
= channel
->channel_info
;
6220 int count
= charOptions
[option
].count
, index
;
6224 index
= atoi(argv
[1]);
6226 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6228 reply("CSMSG_INVALID_NUMERIC", index
);
6229 /* Show possible values. */
6230 for(index
= 0; index
< count
; index
++)
6231 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6235 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6239 /* Find current option value. */
6242 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6246 /* Somehow, the option value is corrupt; reset it to the default. */
6247 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6252 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6256 static MODCMD_FUNC(chan_opt_automode
)
6258 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6261 static MODCMD_FUNC(chan_opt_protect
)
6263 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6266 static MODCMD_FUNC(chan_opt_toys
)
6268 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6271 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6273 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6276 static MODCMD_FUNC(chan_opt_bantimeout
)
6278 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6281 static MODCMD_FUNC(chan_opt_topicrefresh
)
6283 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6286 static MODCMD_FUNC(chan_opt_resync
)
6288 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6291 static struct svccmd_list set_shows_list
;
6294 handle_svccmd_unbind(struct svccmd
*target
) {
6296 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6297 if(target
== set_shows_list
.list
[ii
])
6298 set_shows_list
.used
= 0;
6301 static CHANSERV_FUNC(cmd_set
)
6303 struct svccmd
*subcmd
;
6307 /* Check if we need to (re-)initialize set_shows_list. */
6308 if(!set_shows_list
.used
)
6310 if(!set_shows_list
.size
)
6312 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6313 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6315 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6317 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6318 sprintf(buf
, "%s %s", argv
[0], name
);
6319 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6322 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6325 svccmd_list_append(&set_shows_list
, subcmd
);
6331 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6332 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6334 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6336 subcmd
= set_shows_list
.list
[ii
];
6337 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6339 reply("CSMSG_CHANNEL_OPTIONS_END");
6343 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6344 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6347 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6350 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6352 reply("CSMSG_NO_ACCESS");
6356 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6360 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6362 struct userData
*uData
;
6364 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6367 reply("CSMSG_NOT_USER", channel
->name
);
6373 /* Just show current option value. */
6375 else if(enabled_string(argv
[1]))
6377 uData
->flags
|= mask
;
6379 else if(disabled_string(argv
[1]))
6381 uData
->flags
&= ~mask
;
6385 reply("MSG_INVALID_BINARY", argv
[1]);
6389 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6393 static MODCMD_FUNC(user_opt_autoop
)
6395 struct userData
*uData
;
6397 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6400 reply("CSMSG_NOT_USER", channel
->name
);
6403 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6404 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6406 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6409 static MODCMD_FUNC(user_opt_autoinvite
)
6411 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6414 static MODCMD_FUNC(user_opt_autojoin
)
6416 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6419 static MODCMD_FUNC(user_opt_info
)
6421 struct userData
*uData
;
6424 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6428 /* If they got past the command restrictions (which require access)
6429 * but fail this test, we have some fool with security override on.
6431 reply("CSMSG_NOT_USER", channel
->name
);
6438 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6439 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6441 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6444 bp
= strcspn(infoline
, "\001");
6447 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6452 if(infoline
[0] == '*' && infoline
[1] == 0)
6455 uData
->info
= strdup(infoline
);
6458 reply("CSMSG_USET_INFO", uData
->info
);
6460 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6464 struct svccmd_list uset_shows_list
;
6466 static CHANSERV_FUNC(cmd_uset
)
6468 struct svccmd
*subcmd
;
6472 /* Check if we need to (re-)initialize uset_shows_list. */
6473 if(!uset_shows_list
.used
)
6477 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6480 if(!uset_shows_list
.size
)
6482 uset_shows_list
.size
= ArrayLength(options
);
6483 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6485 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6487 const char *name
= options
[ii
];
6488 sprintf(buf
, "%s %s", argv
[0], name
);
6489 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6492 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6495 svccmd_list_append(&uset_shows_list
, subcmd
);
6501 /* Do this so options are presented in a consistent order. */
6502 reply("CSMSG_USER_OPTIONS");
6503 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6504 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6508 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6509 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6512 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6516 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6519 static CHANSERV_FUNC(cmd_giveownership
)
6521 struct handle_info
*new_owner_hi
;
6522 struct userData
*new_owner
, *curr_user
;
6523 struct chanData
*cData
= channel
->channel_info
;
6524 struct do_not_register
*dnr
;
6525 struct giveownership
*giveownership
;
6526 unsigned int force
, override
;
6527 unsigned short co_access
, new_owner_old_access
;
6528 char transfer_reason
[MAXLEN
];
6531 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6532 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6534 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6535 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6536 && (uData
->access
> 500)
6537 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6538 || uData
->access
< 500));
6541 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6543 struct userData
*owner
= NULL
;
6544 for(curr_user
= channel
->channel_info
->users
;
6546 curr_user
= curr_user
->next
)
6548 if(curr_user
->access
!= UL_OWNER
)
6552 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6559 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6561 char delay
[INTERVALLEN
];
6562 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6563 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6567 reply("CSMSG_NO_OWNER", channel
->name
);
6570 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6572 if(new_owner_hi
== user
->handle_info
)
6574 reply("CSMSG_NO_TRANSFER_SELF");
6577 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6582 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
, 0);
6586 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6590 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6592 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6595 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6596 if(!IsHelping(user
))
6597 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6599 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6603 new_owner_old_access
= new_owner
->access
;
6604 if(new_owner
->access
>= UL_COOWNER
)
6605 co_access
= new_owner
->access
;
6607 co_access
= UL_COOWNER
;
6608 new_owner
->access
= UL_OWNER
;
6610 curr_user
->access
= co_access
;
6611 cData
->ownerTransfer
= now
;
6613 giveownership
= calloc(1, sizeof(*giveownership
));
6614 giveownership
->issued
= now
;
6615 giveownership
->old_owner
= curr_user
->handle
->handle
;
6616 giveownership
->target
= new_owner_hi
->handle
;
6617 giveownership
->target_access
= new_owner_old_access
;
6620 if(argc
> (2 + force
))
6622 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6623 giveownership
->reason
= strdup(transfer_reason
);
6625 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6628 giveownership
->previous
= channel
->channel_info
->giveownership
;
6629 channel
->channel_info
->giveownership
= giveownership
;
6631 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6632 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6633 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6638 chanserv_expire_user_suspension(void *data
)
6640 struct userData
*target
= data
;
6642 target
->expires
= 0;
6643 target
->flags
&= ~USER_SUSPENDED
;
6646 static CHANSERV_FUNC(cmd_suspend
)
6648 struct handle_info
*hi
;
6649 struct userData
*self
, *target
;
6653 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6654 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6655 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6657 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6660 if(target
->access
>= self
->access
)
6662 reply("MSG_USER_OUTRANKED", hi
->handle
);
6665 if(target
->flags
& USER_SUSPENDED
)
6667 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6672 target
->present
= 0;
6675 if(!strcmp(argv
[2], "0"))
6679 unsigned int duration
;
6680 if(!(duration
= ParseInterval(argv
[2])))
6682 reply("MSG_INVALID_DURATION", argv
[2]);
6685 expiry
= now
+ duration
;
6688 target
->expires
= expiry
;
6691 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6693 target
->flags
|= USER_SUSPENDED
;
6694 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6698 static CHANSERV_FUNC(cmd_unsuspend
)
6700 struct handle_info
*hi
;
6701 struct userData
*self
, *target
;
6704 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6705 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6706 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6708 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6711 if(target
->access
>= self
->access
)
6713 reply("MSG_USER_OUTRANKED", hi
->handle
);
6716 if(!(target
->flags
& USER_SUSPENDED
))
6718 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6721 target
->flags
&= ~USER_SUSPENDED
;
6722 scan_user_presence(target
, NULL
);
6723 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6724 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6728 static MODCMD_FUNC(cmd_deleteme
)
6730 struct handle_info
*hi
;
6731 struct userData
*target
;
6732 const char *confirm_string
;
6733 unsigned short access
;
6736 hi
= user
->handle_info
;
6737 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6739 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6742 if(target
->access
== UL_OWNER
)
6744 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6747 confirm_string
= make_confirmation_string(target
);
6748 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6750 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6753 access
= target
->access
;
6754 channel_name
= strdup(channel
->name
);
6755 del_channel_user(target
, 1);
6756 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6762 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6764 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6765 struct chanData
*cData
;
6768 for(cData
= channelList
; cData
; cData
= cData
->next
)
6770 if(IsSuspended(cData
))
6772 opt
= cData
->chOpts
[chTopicRefresh
];
6775 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6778 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6779 cData
->last_refresh
= refresh_num
;
6781 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6785 chanserv_auto_resync(UNUSED_ARG(void *data
))
6787 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6788 struct chanData
*cData
;
6791 for(cData
= channelList
; cData
; cData
= cData
->next
)
6793 if(IsSuspended(cData
)) continue;
6794 opt
= cData
->chOpts
[chResync
];
6795 if(opt
== 'n') continue;
6796 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6797 resync_channel(cData
->channel
);
6798 cData
->last_resync
= refresh_num
;
6800 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6803 static CHANSERV_FUNC(cmd_unf
)
6807 char response
[MAXLEN
];
6808 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6809 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6810 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6813 reply("CSMSG_UNF_RESPONSE");
6817 static CHANSERV_FUNC(cmd_ping
)
6821 char response
[MAXLEN
];
6822 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6823 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6824 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6827 reply("CSMSG_PING_RESPONSE");
6831 static CHANSERV_FUNC(cmd_wut
)
6835 char response
[MAXLEN
];
6836 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6837 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6838 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6841 reply("CSMSG_WUT_RESPONSE");
6846 static CHANSERV_FUNC(cmd_8ball
)
6848 unsigned int i
, j
, accum
;
6853 for(i
=1; i
<argc
; i
++)
6854 for(j
=0; argv
[i
][j
]; j
++)
6855 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6856 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6859 char response
[MAXLEN
];
6860 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6861 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6864 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6868 #else /* Use cool 8ball instead */
6870 void eightball(char *outcome
, int method
, unsigned int seed
)
6874 #define NUMOFCOLORS 18
6875 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6876 "white", "black", "grey", "brown",
6877 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6878 "fuchsia","turquoise","magenta", "cyan"};
6879 #define NUMOFLOCATIONS 50
6880 char balllocations
[50][55] = {
6881 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6882 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6883 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6884 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6885 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6886 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6887 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6888 "your bra", "your hair", "your bed", "the couch", "the wall",
6889 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6890 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6891 #define NUMOFPREPS 15
6892 char ballpreps
[50][50] = {
6893 "Near", "Somewhere near", "In", "In", "In",
6894 "In", "Hiding in", "Under", "Next to", "Over",
6895 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6896 #define NUMOFNUMS 34
6897 char ballnums
[50][50] = {
6898 "A hundred", "A thousand", "A few", "42",
6899 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6900 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6901 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6903 #define NUMOFMULTS 8
6904 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6907 * 0: normal (Not used in x3)
6914 if (method
== 1) /* A Color */
6918 answer
= (rand() % 12); /* Make sure this is the # of entries */
6921 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6923 case 1: strcpy(tmp
, "Sort of a light %s color.");
6925 case 2: strcpy(tmp
, "Dark and dreary %s.");
6927 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6929 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6931 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6933 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6935 case 10: strcpy(tmp
, "Solid %s.");
6937 case 11: strcpy(tmp
, "Transparent %s.");
6939 default: strcpy(outcome
, "An invalid random number was generated.");
6942 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6945 else if (method
== 2) /* Location */
6947 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6949 else if (method
== 3) /* Number of ___ */
6951 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6955 //Debug(DBGWARNING, "Error in 8ball.");
6960 static CHANSERV_FUNC(cmd_8ball
)
6962 char *word1
, *word2
, *word3
;
6963 static char eb
[MAXLEN
];
6964 unsigned int accum
, i
, j
;
6968 for(i
=1; i
<argc
; i
++)
6969 for(j
=0; argv
[i
][j
]; j
++)
6970 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6972 accum
+= time(NULL
)/3600;
6974 word2
= argc
>2?argv
[2]:"";
6975 word3
= argc
>3?argv
[3]:"";
6978 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6979 eightball(eb
, 1, accum
);
6980 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6981 eightball(eb
, 1, accum
);
6982 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6983 eightball(eb
, 1, accum
);
6984 /*** LOCATION *****/
6989 (strcasecmp(word1
, "where") == 0) &&
6990 (strcasecmp(word2
, "is") == 0)
6994 strcasecmp(word1
, "where's") == 0
6997 eightball(eb
, 2, accum
);
6999 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
7000 eightball(eb
, 3, accum
);
7004 /* Generic 8ball question.. so pull from x3.conf srvx style */
7007 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7010 char response
[MAXLEN
];
7011 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
7012 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7015 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7021 char response
[MAXLEN
];
7022 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
7023 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7026 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
7031 static CHANSERV_FUNC(cmd_d
)
7033 unsigned long sides
, count
, modifier
, ii
, total
;
7034 char response
[MAXLEN
], *sep
;
7038 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
7048 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
7049 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
7053 else if((sep
[0] == '-') && isdigit(sep
[1]))
7054 modifier
= strtoul(sep
, NULL
, 10);
7055 else if((sep
[0] == '+') && isdigit(sep
[1]))
7056 modifier
= strtoul(sep
+1, NULL
, 10);
7063 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
7068 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7071 for(total
= ii
= 0; ii
< count
; ++ii
)
7072 total
+= (rand() % sides
) + 1;
7075 if((count
> 1) || modifier
)
7077 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7078 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7082 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7083 sprintf(response
, fmt
, total
, sides
);
7086 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7088 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7092 static CHANSERV_FUNC(cmd_huggle
)
7094 /* CTCP must be via PRIVMSG, never notice */
7096 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7098 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7102 static CHANSERV_FUNC(cmd_calc
)
7104 char response
[MAXLEN
];
7107 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7110 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7112 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7116 static CHANSERV_FUNC(cmd_reply
)
7120 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7123 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7125 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7130 chanserv_adjust_limit(void *data
)
7132 struct mod_chanmode change
;
7133 struct chanData
*cData
= data
;
7134 struct chanNode
*channel
= cData
->channel
;
7137 if(IsSuspended(cData
))
7140 cData
->limitAdjusted
= now
;
7141 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7142 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7144 if(limit
> cData
->modes
.new_limit
)
7145 limit
= cData
->modes
.new_limit
;
7146 else if(limit
== cData
->modes
.new_limit
)
7150 mod_chanmode_init(&change
);
7151 change
.modes_set
= MODE_LIMIT
;
7152 change
.new_limit
= limit
;
7153 mod_chanmode_announce(chanserv
, channel
, &change
);
7157 handle_new_channel(struct chanNode
*channel
)
7159 struct chanData
*cData
;
7161 if(!(cData
= channel
->channel_info
))
7164 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7165 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7167 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7168 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7172 check_bans(struct userNode
*user
, const char *channel
)
7174 struct chanNode
*chan
;
7175 struct mod_chanmode change
;
7176 struct chanData
*cData
;
7177 struct banData
*bData
;
7179 if (!(chan
= GetChannel(channel
)))
7182 if(!(cData
= chan
->channel_info
))
7185 mod_chanmode_init(&change
);
7188 if(chan
->banlist
.used
< MAXBANS
)
7190 /* Not joining through a ban. */
7191 for(bData
= cData
->bans
;
7192 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7193 bData
= bData
->next
);
7197 char kick_reason
[MAXLEN
];
7198 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7200 bData
->triggered
= now
;
7201 if(bData
!= cData
->bans
)
7203 /* Shuffle the ban to the head of the list. */
7205 bData
->next
->prev
= bData
->prev
;
7207 bData
->prev
->next
= bData
->next
;
7210 bData
->next
= cData
->bans
;
7213 cData
->bans
->prev
= bData
;
7215 cData
->bans
= bData
;
7218 change
.args
[0].mode
= MODE_BAN
;
7219 change
.args
[0].u
.hostmask
= bData
->mask
;
7220 mod_chanmode_announce(chanserv
, chan
, &change
);
7221 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
7229 /* Welcome to my worst nightmare. Warning: Read (or modify)
7230 the code below at your own risk. */
7232 handle_join(struct modeNode
*mNode
)
7234 struct mod_chanmode change
;
7235 struct userNode
*user
= mNode
->user
;
7236 struct chanNode
*channel
= mNode
->channel
;
7237 struct chanData
*cData
;
7238 struct userData
*uData
= NULL
;
7239 struct banData
*bData
;
7240 struct handle_info
*handle
;
7241 unsigned int modes
= 0, info
= 0;
7244 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7247 cData
= channel
->channel_info
;
7248 if(channel
->members
.used
> cData
->max
)
7249 cData
->max
= channel
->members
.used
;
7252 /* Check for bans. If they're joining through a ban, one of two
7254 * 1: Join during a netburst, by riding the break. Kick them
7255 * unless they have ops or voice in the channel.
7256 * 2: They're allowed to join through the ban (an invite in
7257 * ircu2.10, or a +e on Hybrid, or something).
7258 * If they're not joining through a ban, and the banlist is not
7259 * full, see if they're on the banlist for the channel. If so,
7262 if(user
->uplink
->burst
&& !mNode
->modes
)
7265 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7267 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7269 /* Riding a netburst. Naughty. */
7270 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7277 if(user
->handle_info
)
7279 handle
= user
->handle_info
;
7282 uData
= GetTrueChannelAccess(cData
, handle
);
7287 mod_chanmode_init(&change
);
7290 /* TODO: maybe only people above inviteme level? -Rubin */
7291 /* We don't kick people with access */
7294 if(channel
->banlist
.used
< MAXBANS
)
7296 /* Not joining through a ban. */
7297 for(bData
= cData
->bans
;
7298 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7299 bData
= bData
->next
);
7303 char kick_reason
[MAXLEN
];
7304 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7306 bData
->triggered
= now
;
7307 if(bData
!= cData
->bans
)
7309 /* Shuffle the ban to the head of the list. */
7311 bData
->next
->prev
= bData
->prev
;
7313 bData
->prev
->next
= bData
->next
;
7316 bData
->next
= cData
->bans
;
7319 cData
->bans
->prev
= bData
;
7320 cData
->bans
= bData
;
7323 change
.args
[0].mode
= MODE_BAN
;
7324 change
.args
[0].u
.hostmask
= bData
->mask
;
7325 mod_chanmode_announce(chanserv
, channel
, &change
);
7326 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7332 /* ChanServ will not modify the limits in join-flooded channels.
7333 It will also skip DynLimit processing when the user (or srvx)
7334 is bursting in, because there are likely more incoming. */
7335 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7336 && !user
->uplink
->burst
7337 && !channel
->join_flooded
7338 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7340 /* The user count has begun "bumping" into the channel limit,
7341 so set a timer to raise the limit a bit. Any previous
7342 timers are removed so three incoming users within the delay
7343 results in one limit change, not three. */
7345 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7346 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7349 /* Give automodes exept during join-floods */
7350 if(!channel
->join_flooded
)
7352 if(cData
->chOpts
[chAutomode
] == 'v')
7353 modes
|= MODE_VOICE
;
7354 else if(cData
->chOpts
[chAutomode
] == 'h')
7355 modes
|= MODE_HALFOP
;
7356 else if(cData
->chOpts
[chAutomode
] == 'o')
7357 modes
|= MODE_CHANOP
;
7360 greeting
= cData
->greeting
;
7361 if(user
->handle_info
)
7363 /* handle = user->handle_info; */
7365 if(IsHelper(user
) && !IsHelping(user
))
7368 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7370 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7372 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7378 /* uData = GetTrueChannelAccess(cData, handle); */
7379 if(uData
&& !IsUserSuspended(uData
))
7381 /* non users getting automodes are handled above. */
7382 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7384 /* just op everyone with access */
7385 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7386 modes
|= MODE_VOICE
;
7387 /* or do their access level */
7388 else if(uData
->access
>= UL_OP
)
7389 modes
|= MODE_CHANOP
;
7390 else if(uData
->access
>= UL_HALFOP
)
7391 modes
|= MODE_HALFOP
;
7392 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7393 modes
|= MODE_VOICE
;
7395 if(uData
->access
>= UL_PRESENT
)
7396 cData
->visited
= now
;
7397 if(cData
->user_greeting
)
7398 greeting
= cData
->user_greeting
;
7400 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7401 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7409 /* If user joining normally (not during burst), apply op or voice,
7410 * and send greeting/userinfo as appropriate.
7412 if(!user
->uplink
->burst
)
7416 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7417 if(modes & MODE_CHANOP) {
7418 modes &= ~MODE_HALFOP;
7419 modes &= ~MODE_VOICE;
7422 change
.args
[0].mode
= modes
;
7423 change
.args
[0].u
.member
= mNode
;
7424 mod_chanmode_announce(chanserv
, channel
, &change
);
7427 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7429 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7435 chanserv_autojoin_channels(struct userNode
*user
)
7437 struct userData
*channel
;
7439 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7441 struct chanNode
*cn
;
7442 struct modeNode
*mn
;
7444 if(IsUserSuspended(channel
)
7445 || IsSuspended(channel
->channel
)
7446 || !(cn
= channel
->channel
->channel
))
7449 mn
= GetUserMode(cn
, user
);
7452 if(!IsUserSuspended(channel
)
7453 && IsUserAutoJoin(channel
)
7454 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7456 && !user
->uplink
->burst
)
7457 irc_svsjoin(chanserv
, user
, cn
);
7463 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7465 struct mod_chanmode change
;
7466 struct userData
*channel
;
7467 unsigned int ii
, jj
, i
;
7469 if(!user
->handle_info
)
7472 mod_chanmode_init(&change
);
7474 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7476 struct chanNode
*cn
;
7477 struct chanData
*cData
;
7478 struct modeNode
*mn
;
7479 if(IsUserSuspended(channel
)
7480 || IsSuspended(channel
->channel
)
7481 || !(cn
= channel
->channel
->channel
))
7484 cData
= cn
->channel_info
;
7485 mn
= GetUserMode(cn
, user
);
7488 if(!IsUserSuspended(channel
)
7489 && IsUserAutoInvite(channel
)
7490 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7492 && !user
->uplink
->burst
)
7493 irc_invite(chanserv
, user
, cn
);
7497 if(channel
->access
>= UL_PRESENT
)
7498 channel
->channel
->visited
= now
;
7500 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
7502 if(channel
->access
>= UL_OP
)
7503 change
.args
[0].mode
= MODE_CHANOP
;
7504 else if(channel
->access
>= UL_HALFOP
)
7505 change
.args
[0].mode
= MODE_HALFOP
;
7506 else if(channel
->access
>= UL_PEON
)
7507 change
.args
[0].mode
= MODE_VOICE
;
7509 change
.args
[0].mode
= 0;
7510 change
.args
[0].u
.member
= mn
;
7511 if(change
.args
[0].mode
)
7512 mod_chanmode_announce(chanserv
, cn
, &change
);
7515 channel
->seen
= now
;
7516 channel
->present
= 1;
7519 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7521 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7522 struct banData
*ban
;
7524 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7525 || !channel
->channel_info
7526 || IsSuspended(channel
->channel_info
))
7528 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7530 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7531 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7533 if(jj
< channel
->banlist
.used
)
7535 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7537 char kick_reason
[MAXLEN
];
7538 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7540 change
.args
[0].mode
= MODE_BAN
;
7541 change
.args
[0].u
.hostmask
= ban
->mask
;
7542 mod_chanmode_announce(chanserv
, channel
, &change
);
7543 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7544 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7545 ban
->triggered
= now
;
7550 if(IsSupportHelper(user
))
7552 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7554 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7556 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7562 if (user
->handle_info
->ignores
->used
) {
7563 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7564 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7568 if (user
->handle_info
->epithet
)
7569 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7571 /* process autojoin channels 5 seconds later as this sometimes
7572 happens before autohide */
7573 // timeq_add(now + 5, chanserv_autojoin_channels, user);
7574 chanserv_autojoin_channels(user
);
7578 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7580 struct chanData
*cData
;
7581 struct userData
*uData
;
7583 cData
= mn
->channel
->channel_info
;
7584 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7587 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7589 /* Allow for a bit of padding so that the limit doesn't
7590 track the user count exactly, which could get annoying. */
7591 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7593 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7594 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7598 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7600 scan_user_presence(uData
, mn
->user
);
7604 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7606 unsigned int ii
, jj
;
7607 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7609 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7610 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7612 if(jj
< mn
->user
->channels
.used
)
7615 if(ii
== chanserv_conf
.support_channels
.used
)
7616 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7621 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7623 struct userData
*uData
;
7625 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7626 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7627 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7630 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
7632 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7633 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7636 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7641 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7643 struct chanData
*cData
;
7645 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7648 cData
= channel
->channel_info
;
7649 if(bad_topic(channel
, user
, channel
->topic
))
7650 { /* User doesnt have privs to set topics. Undo it */
7651 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7652 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7655 /* If there is a topic mask set, and the new topic doesnt match,
7656 * set the topic to mask + new_topic */
7657 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7659 char new_topic
[TOPICLEN
+1];
7660 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7663 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7664 /* and fall through to topicsnarf code below.. */
7666 else /* Topic couldnt fit into mask, was too long */
7668 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7669 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7670 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7674 /* With topicsnarf, grab the topic and save it as the default topic. */
7675 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7678 cData
->topic
= strdup(channel
->topic
);
7684 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7686 struct mod_chanmode
*bounce
= NULL
;
7687 unsigned int bnc
, ii
;
7690 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7693 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7694 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7696 char correct
[MAXLEN
];
7697 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7698 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7699 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7701 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7703 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7705 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7706 if(!protect_user(victim
, user
, channel
->channel_info
, false))
7709 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7712 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7713 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7714 if(bounce
->args
[bnc
].u
.member
)
7718 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7719 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7721 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7723 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7725 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7726 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7729 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7730 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7731 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7734 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7736 const char *ban
= change
->args
[ii
].u
.hostmask
;
7737 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7740 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7741 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7742 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7744 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7749 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7750 mod_chanmode_announce(chanserv
, channel
, bounce
);
7751 for(ii
= 0; ii
< change
->argc
; ++ii
)
7752 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7753 free((char*)bounce
->args
[ii
].u
.hostmask
);
7754 mod_chanmode_free(bounce
);
7759 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7761 struct chanNode
*channel
;
7762 struct banData
*bData
;
7763 struct mod_chanmode change
;
7764 unsigned int ii
, jj
;
7765 char kick_reason
[MAXLEN
];
7767 mod_chanmode_init(&change
);
7769 change
.args
[0].mode
= MODE_BAN
;
7770 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7772 channel
= user
->channels
.list
[ii
]->channel
;
7773 /* Need not check for bans if they're opped or voiced. */
7774 /* TODO: does this make sense in automode v, h, and o? *
7775 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7776 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7778 /* Need not check for bans unless channel registration is active. */
7779 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7781 /* Look for a matching ban already on the channel. */
7782 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7783 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7785 /* Need not act if we found one. */
7786 if(jj
< channel
->banlist
.used
)
7788 /* don't kick someone on the userlist */
7789 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7791 /* Look for a matching ban in this channel. */
7792 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7794 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7796 change
.args
[0].u
.hostmask
= bData
->mask
;
7797 mod_chanmode_announce(chanserv
, channel
, &change
);
7798 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7799 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7800 bData
->triggered
= now
;
7801 break; /* we don't need to check any more bans in the channel */
7806 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7808 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7812 dict_remove2(handle_dnrs
, old_handle
, 1);
7813 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7814 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7819 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7821 struct userNode
*h_user
;
7823 if(handle
->channels
)
7825 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7826 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7828 while(handle
->channels
)
7829 del_channel_user(handle
->channels
, 1);
7834 handle_server_link(UNUSED_ARG(struct server
*server
))
7836 struct chanData
*cData
;
7838 for(cData
= channelList
; cData
; cData
= cData
->next
)
7840 if(!IsSuspended(cData
))
7841 cData
->may_opchan
= 1;
7842 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7843 && !cData
->channel
->join_flooded
7844 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7845 < chanserv_conf
.adjust_threshold
))
7847 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7848 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7854 chanserv_conf_read(void)
7858 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7859 struct mod_chanmode
*change
;
7860 struct string_list
*strlist
;
7861 struct chanNode
*chan
;
7864 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7866 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7869 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7870 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7871 chanserv_conf
.support_channels
.used
= 0;
7872 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7874 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7876 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7879 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7881 channelList_append(&chanserv_conf
.support_channels
, chan
);
7884 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7887 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7890 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7892 channelList_append(&chanserv_conf
.support_channels
, chan
);
7894 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7895 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7896 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7897 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7898 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7899 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7900 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7901 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7902 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7903 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7904 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7905 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7906 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7907 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7908 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7909 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7910 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7911 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7912 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7913 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7914 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7915 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7916 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7917 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7918 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7920 NickChange(chanserv
, str
, 0);
7921 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7922 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7923 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7924 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7925 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7926 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7927 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7928 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7929 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7930 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7931 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7932 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7933 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7934 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7935 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7936 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7937 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7938 god_timeout
= str
? ParseInterval(str
) : 60*15;
7939 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7942 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7943 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7944 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7945 && (change
->argc
< 2))
7947 chanserv_conf
.default_modes
= *change
;
7948 mod_chanmode_free(change
);
7950 free_string_list(chanserv_conf
.set_shows
);
7951 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7953 strlist
= string_list_copy(strlist
);
7956 static const char *list
[] = {
7957 /* free form text */
7958 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7959 /* options based on user level */
7960 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7961 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7962 /* multiple choice options */
7963 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7964 /* binary options */
7965 "DynLimit", "NoDelete", "BanTimeout",
7970 strlist
= alloc_string_list(ArrayLength(list
)-1);
7971 for(ii
=0; list
[ii
]; ii
++)
7972 string_list_append(strlist
, strdup(list
[ii
]));
7974 chanserv_conf
.set_shows
= strlist
;
7975 /* We don't look things up now, in case the list refers to options
7976 * defined by modules initialized after this point. Just mark the
7977 * function list as invalid, so it will be initialized.
7979 set_shows_list
.used
= 0;
7980 free_string_list(chanserv_conf
.eightball
);
7981 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7984 strlist
= string_list_copy(strlist
);
7988 strlist
= alloc_string_list(4);
7989 string_list_append(strlist
, strdup("Yes."));
7990 string_list_append(strlist
, strdup("No."));
7991 string_list_append(strlist
, strdup("Maybe so."));
7993 chanserv_conf
.eightball
= strlist
;
7994 free_string_list(chanserv_conf
.old_ban_names
);
7995 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7997 strlist
= string_list_copy(strlist
);
7999 strlist
= alloc_string_list(2);
8000 chanserv_conf
.old_ban_names
= strlist
;
8001 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
8002 off_channel
= str
? atoi(str
) : 0;
8006 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
8009 struct note_type
*ntype
;
8012 if(!(obj
= GET_RECORD_OBJECT(rd
)))
8014 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
8017 if(!(ntype
= chanserv_create_note_type(key
)))
8019 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
8023 /* Figure out set access */
8024 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
8026 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8027 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
8029 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
8031 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
8032 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
8034 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
8036 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
8040 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
8041 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8042 ntype
->set_access
.min_opserv
= 0;
8045 /* Figure out visibility */
8046 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
8047 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8048 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
8049 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8050 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
8051 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
8052 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
8053 ntype
->visible_type
= NOTE_VIS_ALL
;
8055 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8057 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
8058 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
8062 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8064 struct handle_info
*handle
;
8065 struct userData
*uData
;
8066 char *seen
, *inf
, *flags
, *expires
, *expiry
;
8068 unsigned short access
;
8070 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8072 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
8076 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
8077 if(access
> UL_OWNER
)
8079 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
8083 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
8084 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
8085 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
8086 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
8087 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8088 expiry
= database_get_data(rd
->d
.object
, KEY_EXPIRY
, RECDB_QSTRING
);
8089 handle
= get_handle_info(key
);
8092 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
8096 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
, 0);
8097 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
8098 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
8099 uData
->expiry
= expiry
? strtoul(expiry
, NULL
, 0) : 0;
8100 if (uData
->expiry
> 0)
8101 timeq_add(uData
->expiry
, chanserv_expire_tempuser
, uData
);
8103 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
8105 if(uData
->expires
> now
)
8106 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
8108 uData
->flags
&= ~USER_SUSPENDED
;
8111 /* Upgrade: set autoop to the inverse of noautoop */
8112 if(chanserv_read_version
< 2)
8114 /* if noautoop is true, set autoop false, and vice versa */
8115 if(uData
->flags
& USER_NOAUTO_OP
)
8116 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
8118 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
8119 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
);
8125 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8127 struct banData
*bData
;
8128 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
8129 time_t set_time
, triggered_time
, expires_time
;
8131 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8133 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8137 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8138 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8139 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8140 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8141 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8142 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8143 if (!reason
|| !owner
)
8146 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8147 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8149 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8151 expires_time
= set_time
+ atoi(s_duration
);
8155 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8158 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8161 static struct suspended
*
8162 chanserv_read_suspended(dict_t obj
)
8164 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8168 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8169 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8170 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8171 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8172 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8173 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8174 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8175 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8176 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8177 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8181 static struct giveownership
*
8182 chanserv_read_giveownership(dict_t obj
)
8184 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8188 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8189 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8191 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8193 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8194 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8196 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8197 giveownership
->reason
= str
? strdup(str
) : NULL
;
8198 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8199 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8201 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8202 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8203 return giveownership
;
8207 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8209 struct suspended
*suspended
;
8210 struct giveownership
*giveownership
;
8211 struct mod_chanmode
*modes
;
8212 struct chanNode
*cNode
;
8213 struct chanData
*cData
;
8214 struct dict
*channel
, *obj
;
8215 char *str
, *argv
[10];
8219 channel
= hir
->d
.object
;
8221 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8224 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8227 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8230 cData
= register_channel(cNode
, str
);
8233 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8237 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8239 enum levelOption lvlOpt
;
8240 enum charOption chOpt
;
8242 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8243 cData
->flags
= atoi(str
);
8245 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8247 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8249 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8250 else if(levelOptions
[lvlOpt
].old_flag
)
8252 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8253 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8255 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8259 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8261 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8263 cData
->chOpts
[chOpt
] = str
[0];
8266 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8268 enum levelOption lvlOpt
;
8269 enum charOption chOpt
;
8272 cData
->flags
= base64toint(str
, 5);
8273 count
= strlen(str
+= 5);
8274 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8277 if(levelOptions
[lvlOpt
].old_flag
)
8279 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8280 lvl
= levelOptions
[lvlOpt
].flag_value
;
8282 lvl
= levelOptions
[lvlOpt
].default_value
;
8284 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8286 case 'c': lvl
= UL_COOWNER
; break;
8287 case 'm': lvl
= UL_MANAGER
; break;
8288 case 'n': lvl
= UL_OWNER
+1; break;
8289 case 'o': lvl
= UL_OP
; break;
8290 case 'p': lvl
= UL_PEON
; break;
8291 case 'h': lvl
= UL_HALFOP
; break;
8292 case 'w': lvl
= UL_OWNER
; break;
8293 default: lvl
= 0; break;
8295 cData
->lvlOpts
[lvlOpt
] = lvl
;
8297 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8298 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8301 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8303 suspended
= chanserv_read_suspended(obj
);
8304 cData
->suspended
= suspended
;
8305 suspended
->cData
= cData
;
8306 /* We could use suspended->expires and suspended->revoked to
8307 * set the CHANNEL_SUSPENDED flag, but we don't. */
8309 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8311 suspended
= calloc(1, sizeof(*suspended
));
8312 suspended
->issued
= 0;
8313 suspended
->revoked
= 0;
8314 suspended
->suspender
= strdup(str
);
8315 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8316 suspended
->expires
= str
? atoi(str
) : 0;
8317 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8318 suspended
->reason
= strdup(str
? str
: "No reason");
8319 suspended
->previous
= NULL
;
8320 cData
->suspended
= suspended
;
8321 suspended
->cData
= cData
;
8325 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8326 suspended
= NULL
; /* to squelch a warning */
8329 if(IsSuspended(cData
)) {
8330 if(suspended
->expires
> now
)
8331 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8332 else if(suspended
->expires
)
8333 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8336 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8338 giveownership
= chanserv_read_giveownership(obj
);
8339 cData
->giveownership
= giveownership
;
8342 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8343 struct mod_chanmode change
;
8344 mod_chanmode_init(&change
);
8346 change
.args
[0].mode
= MODE_CHANOP
;
8347 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8348 mod_chanmode_announce(chanserv
, cNode
, &change
);
8351 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8352 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8353 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8354 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8355 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8356 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8357 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8358 cData
->max
= str
? atoi(str
) : 0;
8359 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8360 cData
->greeting
= str
? strdup(str
) : NULL
;
8361 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8362 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8363 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8364 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8365 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8366 cData
->topic
= str
? strdup(str
) : NULL
;
8368 if(!IsSuspended(cData
)
8369 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8370 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8371 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8372 cData
->modes
= *modes
;
8374 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8375 if(cData
->modes
.argc
> 1)
8376 cData
->modes
.argc
= 1;
8377 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8378 mod_chanmode_free(modes
);
8381 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8382 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8383 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8385 if(!cData
->users
&& !IsProtected(cData
))
8387 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8388 unregister_channel(cData
, "has empty user list.");
8392 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8393 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8394 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8396 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8397 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8399 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8400 struct record_data
*rd
= iter_data(it
);
8401 const char *note
, *setter
;
8403 if(rd
->type
!= RECDB_OBJECT
)
8405 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8409 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8411 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8413 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8417 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8418 if(!setter
) setter
= "<unknown>";
8419 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8427 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8429 const char *setter
, *reason
, *str
;
8430 struct do_not_register
*dnr
;
8432 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8435 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8438 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8441 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8444 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8447 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8449 dnr
->set
= atoi(str
);
8455 chanserv_version_read(struct dict
*section
)
8459 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8461 chanserv_read_version
= atoi(str
);
8462 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8466 chanserv_saxdb_read(struct dict
*database
)
8468 struct dict
*section
;
8471 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8472 chanserv_version_read(section
);
8474 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8475 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8476 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8478 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8479 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8480 chanserv_channel_read(iter_key(it
), iter_data(it
));
8482 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8483 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8484 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8490 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8492 int high_present
= 0;
8493 saxdb_start_record(ctx
, KEY_USERS
, 1);
8494 for(; uData
; uData
= uData
->next
)
8496 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8498 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8499 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8500 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8501 saxdb_write_int(ctx
, KEY_EXPIRY
, uData
->expiry
);
8503 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8505 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8507 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8508 saxdb_end_record(ctx
);
8510 saxdb_end_record(ctx
);
8511 return high_present
;
8515 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8519 saxdb_start_record(ctx
, KEY_BANS
, 1);
8520 for(; bData
; bData
= bData
->next
)
8522 saxdb_start_record(ctx
, bData
->mask
, 0);
8523 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8524 if(bData
->triggered
)
8525 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8527 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8529 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8531 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8532 saxdb_end_record(ctx
);
8534 saxdb_end_record(ctx
);
8538 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8540 saxdb_start_record(ctx
, name
, 0);
8541 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8542 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8544 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8546 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8548 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8550 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8551 saxdb_end_record(ctx
);
8555 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8557 saxdb_start_record(ctx
, name
, 0);
8558 if(giveownership
->staff_issuer
)
8559 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8560 if(giveownership
->old_owner
)
8561 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8562 if(giveownership
->target
)
8563 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8564 if(giveownership
->target_access
)
8565 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8566 if(giveownership
->reason
)
8567 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8568 if(giveownership
->issued
)
8569 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8570 if(giveownership
->previous
)
8571 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8572 saxdb_end_record(ctx
);
8576 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8580 enum levelOption lvlOpt
;
8581 enum charOption chOpt
;
8583 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8585 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8586 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8588 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8589 if(channel
->registrar
)
8590 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8591 if(channel
->greeting
)
8592 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8593 if(channel
->user_greeting
)
8594 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8595 if(channel
->topic_mask
)
8596 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8597 if(channel
->suspended
)
8598 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8599 if(channel
->giveownership
)
8600 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8602 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8603 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8604 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8605 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8606 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8608 buf
[0] = channel
->chOpts
[chOpt
];
8610 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8612 saxdb_end_record(ctx
);
8614 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8616 mod_chanmode_format(&channel
->modes
, buf
);
8617 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8620 high_present
= chanserv_write_users(ctx
, channel
->users
);
8621 chanserv_write_bans(ctx
, channel
->bans
);
8623 if(dict_size(channel
->notes
))
8627 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8628 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8630 struct note
*note
= iter_data(it
);
8631 saxdb_start_record(ctx
, iter_key(it
), 0);
8632 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8633 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8634 saxdb_end_record(ctx
);
8636 saxdb_end_record(ctx
);
8639 if(channel
->ownerTransfer
)
8640 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8641 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8642 saxdb_end_record(ctx
);
8646 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8650 saxdb_start_record(ctx
, ntype
->name
, 0);
8651 switch(ntype
->set_access_type
)
8653 case NOTE_SET_CHANNEL_ACCESS
:
8654 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8656 case NOTE_SET_CHANNEL_SETTER
:
8657 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8659 case NOTE_SET_PRIVILEGED
: default:
8660 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8663 switch(ntype
->visible_type
)
8665 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8666 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8667 case NOTE_VIS_PRIVILEGED
:
8668 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8670 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8671 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8672 saxdb_end_record(ctx
);
8676 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8678 struct do_not_register
*dnr
;
8681 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8683 dnr
= iter_data(it
);
8684 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8686 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8687 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8688 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8689 saxdb_end_record(ctx
);
8694 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8697 struct chanData
*channel
;
8699 /* Version Control*/
8700 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8701 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8702 saxdb_end_record(ctx
);
8705 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8706 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8707 chanserv_write_note_type(ctx
, iter_data(it
));
8708 saxdb_end_record(ctx
);
8711 saxdb_start_record(ctx
, KEY_DNR
, 1);
8712 write_dnrs_helper(ctx
, handle_dnrs
);
8713 write_dnrs_helper(ctx
, plain_dnrs
);
8714 write_dnrs_helper(ctx
, mask_dnrs
);
8715 saxdb_end_record(ctx
);
8718 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8719 for(channel
= channelList
; channel
; channel
= channel
->next
)
8720 chanserv_write_channel(ctx
, channel
);
8721 saxdb_end_record(ctx
);
8727 chanserv_db_cleanup(void) {
8729 unreg_part_func(handle_part
);
8731 unregister_channel(channelList
, "terminating.");
8732 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8733 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8734 free(chanserv_conf
.support_channels
.list
);
8735 dict_delete(handle_dnrs
);
8736 dict_delete(plain_dnrs
);
8737 dict_delete(mask_dnrs
);
8738 dict_delete(note_types
);
8739 free_string_list(chanserv_conf
.eightball
);
8740 free_string_list(chanserv_conf
.old_ban_names
);
8741 free_string_list(chanserv_conf
.set_shows
);
8742 free(set_shows_list
.list
);
8743 free(uset_shows_list
.list
);
8746 struct userData
*helper
= helperList
;
8747 helperList
= helperList
->next
;
8752 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8753 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8754 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8757 init_chanserv(const char *nick
)
8759 struct chanNode
*chan
;
8761 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8762 conf_register_reload(chanserv_conf_read
);
8764 reg_server_link_func(handle_server_link
);
8766 reg_new_channel_func(handle_new_channel
);
8767 reg_join_func(handle_join
);
8768 reg_part_func(handle_part
);
8769 reg_kick_func(handle_kick
);
8770 reg_topic_func(handle_topic
);
8771 reg_mode_change_func(handle_mode
);
8772 reg_nick_change_func(handle_nick_change
);
8774 reg_auth_func(handle_auth
);
8775 reg_handle_rename_func(handle_rename
);
8776 reg_unreg_func(handle_unreg
);
8778 handle_dnrs
= dict_new();
8779 dict_set_free_data(handle_dnrs
, free
);
8780 plain_dnrs
= dict_new();
8781 dict_set_free_data(plain_dnrs
, free
);
8782 mask_dnrs
= dict_new();
8783 dict_set_free_data(mask_dnrs
, free
);
8785 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8786 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8787 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8788 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8789 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8790 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8791 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8792 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8793 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8794 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8796 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8798 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8799 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8801 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8802 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8803 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8804 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8805 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8807 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8808 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8809 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8810 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8811 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8812 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8814 DEFINE_COMMAND(levels
, 1, 0, NULL
);
8816 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8817 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8818 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8819 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8821 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8822 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8823 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8824 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8825 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8826 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8827 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8828 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8829 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8830 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8832 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8833 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8834 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8835 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8836 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8837 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8838 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8839 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8840 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8841 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8842 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8843 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8844 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8845 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8847 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8848 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8849 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8850 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8852 /* if you change dellamer access, see also places
8853 * like unbanme which have manager hardcoded. */
8854 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8855 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8857 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8859 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8861 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8862 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8863 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8864 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8865 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8866 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8867 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8868 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8869 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8870 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8871 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8872 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8874 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8875 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8877 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8878 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8879 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8880 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8882 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8883 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8884 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8885 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8886 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8888 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8889 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8890 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8891 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8892 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8893 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8894 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8895 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8897 /* Channel options */
8898 DEFINE_CHANNEL_OPTION(defaulttopic
);
8899 DEFINE_CHANNEL_OPTION(topicmask
);
8900 DEFINE_CHANNEL_OPTION(greeting
);
8901 DEFINE_CHANNEL_OPTION(usergreeting
);
8902 DEFINE_CHANNEL_OPTION(modes
);
8903 DEFINE_CHANNEL_OPTION(enfops
);
8904 DEFINE_CHANNEL_OPTION(enfhalfops
);
8905 DEFINE_CHANNEL_OPTION(automode
);
8906 DEFINE_CHANNEL_OPTION(protect
);
8907 DEFINE_CHANNEL_OPTION(enfmodes
);
8908 DEFINE_CHANNEL_OPTION(enftopic
);
8909 DEFINE_CHANNEL_OPTION(pubcmd
);
8910 DEFINE_CHANNEL_OPTION(userinfo
);
8911 DEFINE_CHANNEL_OPTION(dynlimit
);
8912 DEFINE_CHANNEL_OPTION(topicsnarf
);
8913 DEFINE_CHANNEL_OPTION(nodelete
);
8914 DEFINE_CHANNEL_OPTION(toys
);
8915 DEFINE_CHANNEL_OPTION(setters
);
8916 DEFINE_CHANNEL_OPTION(topicrefresh
);
8917 DEFINE_CHANNEL_OPTION(resync
);
8918 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8919 DEFINE_CHANNEL_OPTION(bantimeout
);
8920 DEFINE_CHANNEL_OPTION(inviteme
);
8922 DEFINE_CHANNEL_OPTION(offchannel
);
8923 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8925 /* Alias set topic to set defaulttopic for compatibility. */
8926 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8929 DEFINE_USER_OPTION(autoinvite
);
8930 DEFINE_USER_OPTION(autojoin
);
8931 DEFINE_USER_OPTION(info
);
8932 DEFINE_USER_OPTION(autoop
);
8934 /* Alias uset autovoice to uset autoop. */
8935 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8937 note_types
= dict_new();
8938 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8941 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8942 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8943 service_register(chanserv
)->trigger
= '!';
8944 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8947 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8949 if(chanserv_conf
.channel_expire_frequency
)
8950 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8952 if(chanserv_conf
.ban_timeout_frequency
)
8953 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8955 if(chanserv_conf
.refresh_period
)
8957 time_t next_refresh
;
8958 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8959 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8960 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8963 if (autojoin_channels
&& chanserv
) {
8964 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8965 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8966 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8970 reg_exit_func(chanserv_db_cleanup
);
8971 message_register_table(msgtab
);